include/SDL_assert.h
author Eli Gottlieb <eligottlieb@gmail.com>
Sun, 23 May 2010 01:27:09 -0400
changeset 4767 dc3fd9b7fcbc
parent 3697 f7b03b6838cb
child 5011 d306007299ac
permissions -rw-r--r--
I'm actually going to have to alter this proposed API a fair bit, but it's coming along. I want to keep it as orthogonal to the rest of SDL as possible.
slouken@3647
     1
/*
slouken@3647
     2
    SDL - Simple DirectMedia Layer
slouken@3697
     3
    Copyright (C) 1997-2010 Sam Lantinga
slouken@3647
     4
slouken@3647
     5
    This library is free software; you can redistribute it and/or
slouken@3647
     6
    modify it under the terms of the GNU Lesser General Public
slouken@3647
     7
    License as published by the Free Software Foundation; either
slouken@3647
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@3647
     9
slouken@3647
    10
    This library is distributed in the hope that it will be useful,
slouken@3647
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@3647
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@3647
    13
    Lesser General Public License for more details.
slouken@3647
    14
slouken@3647
    15
    You should have received a copy of the GNU Lesser General Public
slouken@3647
    16
    License along with this library; if not, write to the Free Software
slouken@3647
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@3647
    18
slouken@3647
    19
    Sam Lantinga
slouken@3647
    20
    slouken@libsdl.org
slouken@3647
    21
*/
slouken@3647
    22
slouken@3647
    23
#ifndef _SDL_assert_h
slouken@3647
    24
#define _SDL_assert_h
slouken@3647
    25
slouken@3655
    26
#include "SDL_config.h"
slouken@3655
    27
slouken@3655
    28
#include "begin_code.h"
slouken@3655
    29
/* Set up for C function definitions, even when using C++ */
slouken@3655
    30
#ifdef __cplusplus
slouken@3655
    31
/* *INDENT-OFF* */
slouken@3655
    32
extern "C" {
slouken@3655
    33
/* *INDENT-ON* */
slouken@3655
    34
#endif
slouken@3655
    35
slouken@3647
    36
#ifndef SDL_ASSERT_LEVEL
slouken@3654
    37
#ifdef SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    38
#define SDL_ASSERT_LEVEL SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    39
#elif defined(_DEBUG) || defined(DEBUG) || \
slouken@3654
    40
      (defined(__GNUC__) && !defined(__OPTIMIZE__))
slouken@3653
    41
#define SDL_ASSERT_LEVEL 2
slouken@3653
    42
#else
slouken@3653
    43
#define SDL_ASSERT_LEVEL 1
slouken@3647
    44
#endif
slouken@3653
    45
#endif /* SDL_ASSERT_LEVEL */
slouken@3647
    46
slouken@3647
    47
/*
slouken@3655
    48
These are macros and not first class functions so that the debugger breaks
slouken@3655
    49
on the assertion line and not in some random guts of SDL, and so each
icculus@3665
    50
assert can have unique static variables associated with it.
slouken@3655
    51
*/
slouken@3655
    52
slouken@3655
    53
#if (defined(_MSC_VER) && ((_M_IX86) || (_M_X64)))
slouken@3655
    54
    #define SDL_TriggerBreakpoint() __asm { int 3 }
slouken@3655
    55
#elif (defined(__GNUC__) && ((__i386__) || (__x86_64__)))
slouken@3655
    56
    #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
slouken@3655
    57
#elif defined(HAVE_SIGNAL_H)
slouken@3655
    58
    #include <signal.h>
slouken@3655
    59
    #define SDL_TriggerBreakpoint() raise(SIGTRAP)
slouken@3655
    60
#else
slouken@3655
    61
    /* How do we trigger breakpoints on this platform? */
slouken@3655
    62
    #define SDL_TriggerBreakpoint()
slouken@3655
    63
#endif
slouken@3655
    64
slouken@3655
    65
#if (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
slouken@3655
    66
#   define SDL_FUNCTION __func__
slouken@3655
    67
#elif ((__GNUC__ >= 2) || defined(_MSC_VER))
slouken@3655
    68
#   define SDL_FUNCTION __FUNCTION__
slouken@3655
    69
#else
slouken@3655
    70
#   define SDL_FUNCTION "???"
slouken@3655
    71
#endif
slouken@3655
    72
#define SDL_FILE    __FILE__
slouken@3655
    73
#define SDL_LINE    __LINE__
slouken@3655
    74
slouken@3655
    75
/*
slouken@3647
    76
sizeof (x) makes the compiler still parse the expression even without
slouken@3647
    77
assertions enabled, so the code is always checked at compile time, but
slouken@3647
    78
doesn't actually generate code for it, so there are no side effects or
slouken@3647
    79
expensive checks at run time, just the constant size of what x WOULD be,
slouken@3647
    80
which presumably gets optimized out as unused.
slouken@3647
    81
This also solves the problem of...
slouken@3647
    82
slouken@3647
    83
    int somevalue = blah();
slouken@3647
    84
    SDL_assert(somevalue == 1);
slouken@3647
    85
slouken@3647
    86
...which would cause compiles to complain that somevalue is unused if we
slouken@3647
    87
disable assertions.
slouken@3647
    88
*/
slouken@3647
    89
slouken@3647
    90
#define SDL_disabled_assert(condition) \
slouken@3647
    91
    do { (void) sizeof ((condition)); } while (0)
slouken@3647
    92
slouken@3647
    93
#if (SDL_ASSERT_LEVEL > 0)
slouken@3647
    94
slouken@3647
    95
typedef enum
slouken@3647
    96
{
slouken@3647
    97
    SDL_ASSERTION_RETRY,  /**< Retry the assert immediately. */
slouken@3647
    98
    SDL_ASSERTION_BREAK,  /**< Make the debugger trigger a breakpoint. */
slouken@3647
    99
    SDL_ASSERTION_ABORT,  /**< Terminate the program. */
slouken@3647
   100
    SDL_ASSERTION_IGNORE,  /**< Ignore the assert. */
slouken@3647
   101
    SDL_ASSERTION_ALWAYS_IGNORE,  /**< Ignore the assert from now on. */
slouken@3647
   102
} SDL_assert_state;
slouken@3647
   103
slouken@3647
   104
typedef struct SDL_assert_data
slouken@3647
   105
{
slouken@3647
   106
    int always_ignore;
slouken@3647
   107
    unsigned int trigger_count;
slouken@3647
   108
    const char *condition;
slouken@3647
   109
    const char *filename;
slouken@3647
   110
    int linenum;
slouken@3647
   111
    const char *function;
icculus@3670
   112
    const struct SDL_assert_data *next;
slouken@3647
   113
} SDL_assert_data;
slouken@3647
   114
icculus@3668
   115
/* Never call this directly. Use the SDL_assert* macros. */
slouken@3655
   116
extern DECLSPEC SDL_assert_state SDLCALL SDL_ReportAssertion(SDL_assert_data *,
slouken@3655
   117
                                                             const char *,
slouken@3655
   118
                                                             const char *, int);
slouken@3647
   119
slouken@3647
   120
/* the do {} while(0) avoids dangling else problems:
slouken@3647
   121
    if (x) SDL_assert(y); else blah();
slouken@3647
   122
       ... without the do/while, the "else" could attach to this macro's "if".
slouken@3647
   123
   We try to handle just the minimum we need here in a macro...the loop,
slouken@3647
   124
   the static vars, and break points. The heavy lifting is handled in
slouken@3647
   125
   SDL_ReportAssertion(), in SDL_assert.c.
slouken@3647
   126
*/
slouken@3647
   127
#define SDL_enabled_assert(condition) \
slouken@3647
   128
    do { \
slouken@3647
   129
        while ( !(condition) ) { \
slouken@3649
   130
            static struct SDL_assert_data assert_data = { \
slouken@3655
   131
                0, 0, #condition, 0, 0, 0, 0 \
slouken@3647
   132
            }; \
slouken@3649
   133
            const SDL_assert_state state = SDL_ReportAssertion(&assert_data, \
slouken@3647
   134
                                                               SDL_FUNCTION, \
slouken@3655
   135
                                                               SDL_FILE, \
slouken@3655
   136
                                                               SDL_LINE); \
slouken@3647
   137
            if (state == SDL_ASSERTION_RETRY) { \
slouken@3647
   138
                continue; /* go again. */ \
slouken@3647
   139
            } else if (state == SDL_ASSERTION_BREAK) { \
slouken@3647
   140
                SDL_TriggerBreakpoint(); \
slouken@3647
   141
            } \
slouken@3647
   142
            break; /* not retrying. */ \
slouken@3647
   143
        } \
slouken@3647
   144
    } while (0)
slouken@3647
   145
slouken@3647
   146
#endif  /* enabled assertions support code */
slouken@3647
   147
slouken@3647
   148
/* Enable various levels of assertions. */
slouken@3647
   149
#if SDL_ASSERT_LEVEL == 0   /* assertions disabled */
slouken@3647
   150
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   151
#   define SDL_assert_release(condition) SDL_disabled_assert(condition)
slouken@3647
   152
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   153
#elif SDL_ASSERT_LEVEL == 1  /* release settings. */
slouken@3652
   154
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   155
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3652
   156
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   157
#elif SDL_ASSERT_LEVEL == 2  /* normal settings. */
slouken@3647
   158
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   159
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   160
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   161
#elif SDL_ASSERT_LEVEL == 3  /* paranoid settings. */
slouken@3647
   162
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   163
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   164
#   define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
slouken@3647
   165
#else
slouken@3655
   166
#   error Unknown assertion level.
slouken@3647
   167
#endif
slouken@3647
   168
icculus@3670
   169
icculus@3670
   170
typedef SDL_assert_state (SDLCALL *SDL_AssertionHandler)(
icculus@3670
   171
                                    const SDL_assert_data *, void *userdata);
icculus@3670
   172
icculus@3670
   173
/**
icculus@3670
   174
 *  \brief Set an application-defined assertion handler.
icculus@3670
   175
 *
icculus@3670
   176
 *  This allows an app to show its own assertion UI and/or force the
icculus@3670
   177
 *  response to an assertion failure. If the app doesn't provide this, SDL
icculus@3670
   178
 *  will try to do the right thing, popping up a system-specific GUI dialog,
icculus@3670
   179
 *  and probably minimizing any fullscreen windows.
icculus@3670
   180
 *
icculus@3670
   181
 *  This callback may fire from any thread, but it runs wrapped in a mutex, so
icculus@3670
   182
 *  it will only fire from one thread at a time.
icculus@3670
   183
 *
icculus@3670
   184
 *  Setting the callback to NULL restores SDL's original internal handler.
icculus@3670
   185
 *
icculus@3670
   186
 *  This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
icculus@3670
   187
 *
icculus@3670
   188
 *  \return SDL_assert_state value of how to handle the assertion failure.
icculus@3670
   189
 *  
icculus@3670
   190
 *  \param handler Callback function, called when an assertion fails.
icculus@3670
   191
 *  \param userdata A pointer passed to the callback as-is.
icculus@3670
   192
 */
icculus@3670
   193
extern DECLSPEC void SDLCALL SDL_SetAssertionHandler(
icculus@3670
   194
                                            SDL_AssertionHandler handler,
icculus@3670
   195
                                            void *userdata);
icculus@3670
   196
icculus@3670
   197
/**
icculus@3670
   198
 *  \brief Get a list of all assertion failures.
icculus@3670
   199
 *
icculus@3670
   200
 *  Get all assertions triggered since last call to SDL_ResetAssertionReport(),
icculus@3670
   201
 *  or the start of the program.
icculus@3670
   202
 *
icculus@3670
   203
 *  The proper way to examine this data looks something like this:
icculus@3670
   204
 *
icculus@3670
   205
 *  <code>
icculus@3670
   206
 *  const SDL_assert_data *item = SDL_GetAssertionReport();
icculus@3670
   207
 *  while (item->condition) {
icculus@3670
   208
 *      printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\n",
icculus@3670
   209
 *             item->condition, item->function, item->filename,
icculus@3670
   210
 *             item->linenum, item->trigger_count,
icculus@3670
   211
 *             item->always_ignore ? "yes" : "no");
icculus@3670
   212
 *      item = item->next;
icculus@3670
   213
 *  }
icculus@3670
   214
 *  </code>
icculus@3670
   215
 *
icculus@3670
   216
 *  \return List of all assertions. This never returns NULL,
icculus@3670
   217
 *          even if there are no items.
icculus@3670
   218
 *  \sa SDL_ResetAssertionReport
icculus@3670
   219
 */
icculus@3670
   220
extern DECLSPEC const SDL_assert_data * SDLCALL SDL_GetAssertionReport(void);
icculus@3670
   221
icculus@3670
   222
/**
icculus@3670
   223
 *  \brief Reset the list of all assertion failures.
icculus@3670
   224
 *
icculus@3670
   225
 *  Reset list of all assertions triggered.
icculus@3670
   226
 *
icculus@3670
   227
 *  \sa SDL_GetAssertionReport
icculus@3670
   228
 */
icculus@3670
   229
extern DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);
icculus@3670
   230
slouken@3655
   231
/* Ends C function definitions when using C++ */
slouken@3655
   232
#ifdef __cplusplus
slouken@3655
   233
/* *INDENT-OFF* */
slouken@3655
   234
}
slouken@3655
   235
/* *INDENT-ON* */
slouken@3655
   236
#endif
slouken@3655
   237
#include "close_code.h"
slouken@3655
   238
slouken@3647
   239
#endif /* _SDL_assert_h */
slouken@3647
   240
slouken@3647
   241
/* vi: set ts=4 sw=4 expandtab: */