# EA Assert

A simple lightweight package. Use...

                    #include "EAAssert/eaassert.h"

And get the following macros...

- `EA_ASSERT(expr)` where `expr` is an expression that evaluates to a boolean. For example...

         EA_ASSERT(3 != 5);

- `EA_ASSERT_MSG(expr, msg)` where `msg` is an string (i.e., a `char*`). For example...

         EA_ASSERT_MSG(3 != 5, "mathematics is flawed");

- `EA_ASSERT_FORMATTED(expr, fmt)` where `fmt` is a parentheses wrapped tuple of which the first parameter is a string (i.e., a `char*`) and the rest are `printf` style parameters. For example...

         EA_ASSERT_FORMATTED(3 != 5, ("The answer is: %d", 42));

- `EA_FAIL()` similar to `EA_ASSERT(false)` but avoids warning of constant conditional expression.
- `EA_FAIL_MSG(msg)` similar to `EA_ASSERT_MSG(false, msg);`
- `EA_FAIL_FORMATTED(fmt)` similar to `EA_ASSERT_FORMATTED(false, fmt);`

For compatibility reasons (with existing EATrace practice), there are synonyms for EA_ASSERT_MSG, defined as EA_ASSERT_MESSAGE and EA_ASSERT_M.

## Enabling and Disabling

Whether or not `EA_ASSERT` is enabled hinges on the following logic...

            #ifdef EA_DEBUG
                #define EA_ASSERT_ENABLED
            #endif

            #ifndef EA_ASSERT
    	        #if EA_ASSERT_ENABLED
    		        #define EA_ASSERT(expr) ...definition of EA_ASSERT...
        	    #else
        		    #define EA_ASSERT(expr) ((void)0)
        	    #endif
            #endif

## Design Considerations

Below are some thoughts that went into the creation of this package...

- Aim to be lightweight and simple.
- Become the single most used ubiquitous assert technology, along EA Base lines.
- Have minimal dependencies; e.g. EA Base only.
- Ability for users to override functionality.
- Ability for users to redirect assertions to their application's given system.
- Ability for users to extend the system.
- Enable users to rebuild other technology but have it use their assert system.
- Make it easy to supply your own assert-failure callback function, but...
  - ...don't enforce a link-time definition (i.e., provide a default)
  - ...don't require a global #define.
  The result is that we require a CPP file (and resulting library) where we can declare and store a function pointer that points to the current assert handler.

## Integration

To use `EA_ASSERT` in your own code you just include the header and use it.

To overwrite the default handler for assert failure, you need to implement the following function

            bool EAAssertFailure(

                const char* expr,       // Stringized version of the assert expression.
                const char* filename,   // Name of the file in which the assert occured.
                int line,               // Line number on which the assert occured.
                const char* function,   // Name of the function the assert occured in.
                const char* msg,        // Optional descriptive message.
                va_list args            // Variable argument list (printf style) in case msg contains formatters.

            );

The return value of this function should be `true` if you want the failed assert to break into the debugger and `false` if you want to continue running past the assert.

To enable the use of this function, use:

            EA::Assert::SetFailureCallback(&yourCallback);

The simplest possible implementation of this function (which happens to be the default) just returns `true` immediately, always triggering a debug break.

For reference, here's an example implementation of `EAAssertFailure` that you can copy paste into your own code.

            bool EAAssertFailure(const char* expr, const char* filename, int line, const char* function, const char* msg, va_list args)
            {
                const int size = 1024;
                char output[size + 1] = {};
                char fmtMsg[size + 1] = {};

                _vsnprintf(fmtMsg, size, msg, args);
                _snprintf(output, size, "%s(%d) : assert failed: '%s' in function: %s\n, message: %s", filename, line, expr, function, fmtMsg);

                printf(output);

                return true;
            }

### If You already Have Your Own Asserts

If you already have your own asserts and would like other technology to use yours, you can easily forward `EA_ASSERT` to your own implementation. The trick is in the `#EA_ASSERT_HAVE_OWN_HEADER` macro. Define this to the name of your own header file that has equivalents of the macros defined above (make sure you provide all of them). For example, you could put the following in a file called "game/my_own_awesome_assert.h"...

            #define EA_ASSERT(expr) MY_OWN_AWESOME_ASSERT(expr)
            #define EA_ASSERT_MSG(expr, msg) MY_OWN_AWESOME_ASSERT(expr)
            #define EA_ASSERT_FORMATTED(expr, fmt) MY_OWN_AWESOME_ASSERT(expr)
            #define EA_FAIL() MY_OWN_AWESOME_ASSERT(false)
            #define EA_FAIL_MSG(msg) MY_OWN_AWESOME_ASSERT(false)
            #define EA_FAIL_FORMATTED(fmt) MY_OWN_AWESOME_ASSERT(false)

Next, define `#EA_ASSERT_HAVE_OWN_HEADER` to `"game/my_own_awesome_assert.h"` (including the quotes) when you build `EA_ASSERT` using technology and it will automatically use your assert instead.

## Frequently Asked Questions

- Why don't you supply `EA_WARNING` macros?

  Because few people can use warnings properly. This often results in games spewing so much output to the console that nobody cares anymore. If the problem is serious enough, just assert. If it's not serious enough, put a bright pink texture on it in the game.

- But I really need `EA_WARNING`, can't you add it?

  The truth is, the original author of this package didn't need them. Warning support would increase the complexity of this package of proportion (now you need warning levels, etc.) If you need warnings, write your own.

- I already have my own definitions of the `EA_ASSERT` macro and others, now what?

  Use the `EA_ASSERT_HAVE_OWN_HEADER` feature described above. Now any library that uses `EA_ASSERT` automatically leverages your assert.

- I need multilevel asserts, only enabling some when I really need to. Can you add it?

  This often occurs when you have asserts in a tight inner loop that you only want enabled every now and then when you do very strict integrity checking. Unfortunately it would complicate this library too much. We don't feel enough technologies have a need for this. Fortunately you can implement this yourself fairly easily by doing...

                  #if MY_TECH_DEBUG_LEVEL == 1
                      #define MY_TECH_ASSERT(expr)          EA_ASSERT(expr)
                      #define MY_TECH_HARDCORE_ASSERT(expr) ((void)0)
                  #else if MY_TECH_DEBUG_LEVEL == 1
                      #define MY_TECH_ASSERT(expr)          EA_ASSERT(expr)
                      #define MY_TECH_HARDCORE_ASSERT(expr) EA_ASSERT(expr)
                  #endif

  That way you can still leverage `EA_ASSERT` but have more fine grained control over assert subsets in your technology.

- Why aren't your asserts ignorable?

  Because it would make things more complicated than I'd like. Furthermore, ignorable asserts often flawed. People end up using them to signal user errors that should be handled differently (proper fallback mechanisms, bright pink textures, etc.). What happens is that everybody starts ignoring asserts when they happen rather than fixing them or reporting them. You get away with this for a while, until one day shit hits the fan. Asserts are for programmer error, exceptions and other mechanisms are for user error.

  That said if you desperately need ignorable asserts, all is not lost. Either use the `#EA_ASSERT_HAVE_OWN_HEADER` feature, or simply catch the filename and line-number in the handler function. Stick them as a pair into a set. Whenever you find them in the set, return false from the assert handler to ignore the assert.

- Why isn't platform X supported?

  I don't know. Let me know what its break-into-debugger statement is and I'll gladly add it.

- Why don't you have feature X?

  Probably because I didn't need it. Alternatively, because it would make this package more complicated than I'd like. Perhaps feature X is best implemented in your own technology? Of course, you can always try sending us an email. Maybe we'll add feature X.

## Acknowledgements

Input for this package came from Paul Pedriana, Ian Davids, James Fairweather, Max Burke, Bob Summerwill and others.