#include "Stroika/Foundation/StroikaPreComp.h"
#include "Stroika/Foundation/Common/Common.h"
#include "Stroika/Foundation/Debug/CompileTimeFlagChecker.h"
Go to the source code of this file.
Namespaces | |
namespace | Stroika::Foundation |
Macros | |
#define | qStroika_Foundation_Debug_AssertionsChecked 1 |
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validated (else used as hints for [[assume]]) | |
#define | Stroika_Foundation_Debug_Widen2_(x) L##x |
Stroika_Foundation_Debug_Widen is used mostly internally to expand macros like Stroika_Foundation_Debug_Widen ( FILE) into UNICODE SAFE strings (so debug messages work properly if run on locale with charset other than matching filesystem) Idea from https://stackoverflow.com/questions/3291047/how-do-i-print-the-string-which-file-expands-to-correctly. | |
#define | AssertExpression(c) |
#define | Assert(c) AssertExpression (c) |
#define | RequireExpression(c) |
#define | AssertMember(p, c) Assert (dynamic_cast<const c*> (p) != nullptr) |
#define | EnsureMember(p, c) Ensure (dynamic_cast<const c*> (p) != nullptr) |
#define | RequireMember(p, c) Require (dynamic_cast<const c*> (p) != nullptr) |
#define | AssertNotNull(p) Assert (p != nullptr) |
#define | EnsureNotNull(p) Ensure (p != nullptr) |
#define | RequireNotNull(p) Require (p != nullptr) |
#define | AssertNotReached() |
#define | EnsureNotReached() |
#define | RequireNotReached() |
#define | AssertNotImplemented() |
#define | Verify(c) Assert (c) |
#define | WeakAssert(c) |
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be true. Use this so you see debug logs of rare events you way want to dig into, but don't want to fail/crash the program just because it fails. | |
#define | WeakAssertMember(p, c) WeakAssert (dynamic_cast<const c*> (p) != nullptr) |
#define | WeakAssertNotNull(p) WeakAssert (p != nullptr) |
#define | WeakAssertNotReached() |
#define | WeakAssertNotImplemented() |
#define | WeakVerify(c) WeakAssert (c) |
Typedefs | |
using | Stroika::Foundation::Debug::AssertionHandlerType = void(*)(const wchar_t *assertCategory, const wchar_t *assertionText, const wchar_t *fileName, int lineNum, const wchar_t *functionName) noexcept |
Functions | |
AssertionHandlerType | Stroika::Foundation::Debug::GetAssertionHandler () |
void | Stroika::Foundation::Debug::SetAssertionHandler (AssertionHandlerType assertionHandler) |
void | Stroika::Foundation::Debug::SetWeakAssertionHandler (AssertionHandlerType assertionHandler) |
void | Stroika::Foundation::Debug::SetWeakAssertionHandler (void(*legacyHandler)(const char *assertCategory, const char *assertionText, const char *fileName, int lineNum, const char *functionName) noexcept) |
------------------— DEPRECATED CODE ---------------------— | |
\quote "Truth emerges more readily from error than from confusion."
We chose NOT to do this with Ensure/Assert etc because > they are used so much it would be awkward > they will go away before too long, due to C++26 contracts (I hope)
Definition in file Assertions.h.
#define qStroika_Foundation_Debug_AssertionsChecked 1 |
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validated (else used as hints for [[assume]])
alias qStroika_Foundation_Debug_AssertionsChecked was called qDebug before Stroika v3.0d11
this mechanism will be replaced in future versions of Stroika with 'contracts' (when we support c++26 - so two major releases from now)
Defaults to value determined by other common bug defines (_DEBUG,NDEBUG), or 1.
Assure other common defines for this concept: (_DEBUG,NDEBUG) are set consistently (even if its not explicitly checked).
Definition at line 48 of file Assertions.h.
#define AssertExpression | ( | c | ) |
Like Assert(), but without [[assume]] support, and in the form of an expression (since https://en.cppreference.com/w/cpp/language/attributes/assume - can only be applied to a statement)
result is EXPRESSION;
Definition at line 233 of file Assertions.h.
#define Assert | ( | c | ) | AssertExpression (c) |
Definition at line 256 of file Assertions.h.
#define RequireExpression | ( | c | ) |
Like Require(), but without [[assume]] support, and in the form of an expression (since https://en.cppreference.com/w/cpp/language/attributes/assume - can only be applied to a statement)
result is EXPRESSION;
Definition at line 267 of file Assertions.h.
#define AssertMember | ( | p, | |
c | |||
) | Assert (dynamic_cast<const c*> (p) != nullptr) |
Simple wrapper on Assert () - checking p is a member of class c (with dynamic_cast)
Definition at line 312 of file Assertions.h.
#define EnsureMember | ( | p, | |
c | |||
) | Ensure (dynamic_cast<const c*> (p) != nullptr) |
Simple wrapper on Ensure () - checking p is a member of class c (with dynamic_cast)
Definition at line 319 of file Assertions.h.
#define RequireMember | ( | p, | |
c | |||
) | Require (dynamic_cast<const c*> (p) != nullptr) |
Simple wrapper on Require () - checking p is a member of class c (with dynamic_cast)
Definition at line 326 of file Assertions.h.
#define AssertNotNull | ( | p | ) | Assert (p != nullptr) |
Simple wrapper on Assert () - checking p != nullptr
Definition at line 333 of file Assertions.h.
#define EnsureNotNull | ( | p | ) | Ensure (p != nullptr) |
Simple wrapper on Ensure () - checking p != nullptr
Definition at line 340 of file Assertions.h.
#define RequireNotNull | ( | p | ) | Require (p != nullptr) |
Simple wrapper on Require () - checking p != nullptr
Definition at line 347 of file Assertions.h.
#define AssertNotReached | ( | ) |
A program bug within the procedure called from, if this code is ever reached. In release builds, this calls unreachable, so can be optimized/assumed never reached.
Definition at line 355 of file Assertions.h.
#define EnsureNotReached | ( | ) |
A program bug within the procedure called from, if this code is ever reached. In release builds, this calls unreachable, so can be optimized/assumed never reached.
Definition at line 370 of file Assertions.h.
#define RequireNotReached | ( | ) |
A program bug within the caller, if this code is ever reached. In release builds, this calls unreachable, so can be optimized/assumed on argument condition.
Definition at line 385 of file Assertions.h.
#define AssertNotImplemented | ( | ) |
Use this to mark code that is not yet implemented. Using this name for sections of code which fail because of not being implemented makes it easier to search for such code, and when something breaks (esp during porting) - its easier to see why
Definition at line 401 of file Assertions.h.
#define Verify | ( | c | ) | Assert (c) |
Verify () is an 'assertion' like Assert, except its argument is ALWAYS EVALUATED, even if debug is OFF. This is useful for cases where you just want todo an assertion about the result of a function, but don't want to keep the result in a temporary just to look at it for this one assertion test...
Definition at line 419 of file Assertions.h.
#define WeakAssert | ( | c | ) |
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be true. Use this so you see debug logs of rare events you way want to dig into, but don't want to fail/crash the program just because it fails.
Definition at line 438 of file Assertions.h.
#define WeakAssertMember | ( | p, | |
c | |||
) | WeakAssert (dynamic_cast<const c*> (p) != nullptr) |
Definition at line 452 of file Assertions.h.
#define WeakAssertNotNull | ( | p | ) | WeakAssert (p != nullptr) |
Definition at line 459 of file Assertions.h.
#define WeakAssertNotReached | ( | ) |
Definition at line 467 of file Assertions.h.
#define WeakAssertNotImplemented | ( | ) |
Use this to mark code that is not yet implemented. Using this name for sections of code which fail because of not being implemented makes it easier to search for such code, and when something breaks (esp during porting) - its easier to see why
Definition at line 483 of file Assertions.h.
#define WeakVerify | ( | c | ) | WeakAssert (c) |
Verify () is an assertion like Assert, except its argument is ALWAYS EVALUATED, even if debug is OFF. This is useful for cases where you just want todo an assertion about the result of a function, but don't want to keep the result in a temporary just to look at it for this one assertion test...
Definition at line 502 of file Assertions.h.
using Stroika::Foundation::Debug::AssertionHandlerType = typedef void (*) (const wchar_t* assertCategory, const wchar_t* assertionText, const wchar_t* fileName, int lineNum, const wchar_t* functionName) noexcept |
Note: Some any parameters may be null, if no suitable value is available.
Assertion handlers typically just print a debug message, and then exit the program. They are fatal, and must not return/throw.
Definition at line 82 of file Assertions.h.
AssertionHandlerType Stroika::Foundation::Debug::GetAssertionHandler | ( | ) |
Stroika makes very heavy use of assertions (to some extent inspired and patterned after Bertrand Meyers Eiffel assertion mechanism). Assertions are logical statements about function parameters, results, object states, etc, which are guaranteed (required) to be held true. These logical assertions serve two important functions: they document the requirements on input parameters for a function, and the promises made on function return. And they perform this documentation task in such a way that the compiler can generate special code (conditionally) to verify that the assertions hold true.
This latter point about conditional compilation is important. If the macro preprocessor symbol qStroika_Foundation_Debug_AssertionsChecked
is true (non-zero), then this assertion checking is enabled. If the symbol is false (zero), then the checking is disabled. Of course the promises must always hold true! But since the checking can significantly slow the code, it is best to only build with assertions on in certain circumstances (like while developing, and for much of QA/testing); but for released products it should be disabled so the editor operates at maximum speed.
Stroika's assertion mechanism is not only present to help debug Stroika itself. After all, that would have little value to a user of the Stroika class library. It is also very helpful to a programmer using the class library. This is because nearly all the parameters passed to Stroika functions are immediately checked for validity, so mistakes are trapped as early as possible. If you pass bad values to a Stroika function, you will very likely get a debugger trap at almost exactly the point in your calling code where the error occurred. This can make debugging code written using Stroika much easier.
Stroika provides four families of 'assertion' macro functions. The are named Assert
, Require
, Ensure
, and Verify
.
The family you will most often be interested in is Require
s. This is because these are used to check parameters validity on entry to a function. So typically when you see a Require fail, you will find that it is the calling function which is passing invalid arguments to the function which triggered the requirement failure. The other reason Requires will be of particular interest to programmers using Stroika is because checking the Require declarations at the beginning of a function is often very telling about the details of what sort of parameters the function expects.
Probably the next most common sort of assertion you will see is Ensures. These state promises about the return values of a function. These should very rarely be triggered (if they are , they almost always indicate a bug in the function that triggered the Ensure failure). But the reason they are still of interest to programmers using Stroika is because they document what can be assumed about return values from a particular function.
Plain Assertions are for assertions which don't fall into any of the above categories, but are still useful checks to be performed. When an assertion is triggered, it is almost always diagnostic of a bug in the code which triggered the assertion (or corrupted data structures). (aside: Assertions logically mean the same thing as Ensures, except that Ensures only check return values).
Verifys are inspired by the MFC VERIFY() macro, and the particular idiosyncrasies of the Win32 SDK, though they can be used cross-platform. Many of the Win32 SDK routines return non-zero on success, and zero on failure. Most sample Win32 code you look at simply ignores these results. For paranoia sake (which was very helpful in the old moldy win32s days) I wrap most Win32 SDK calls in a Verify
wrapper. This - when debugging is enabled - checks the return value, and asserts if there is a problem. Very Important: Unlike the other flavors of Assertions, Verify always evaluates its argument!.
This last point is worth repeating, as it is the only source of bugs I've ever seen introduced from the use of assertions (and I've seen the mistake more than once). All flavors of assertions (except Verify) do NOT evaluate their arguments if qStroika_Foundation_Debug_AssertionsChecked
is off. This means you cannot count on the arguments to assertions having any side-effects. Use Verify instead of the other assertion flavors if you want to only check for compliance if qStroika_Foundation_Debug_AssertionsChecked
is true, but want the side-effect to happen regardless.
Now when assertions are triggered, just what happens? Here I think there is only one sensible answer. And that is that the program drops into the debugger. And what happens after that is undefined. This is Stroika's default behavior.
But there are others who hold the view that triggered assertions should generate exceptions. This isn't an appropriate forum for explanations of why this is generally a bad idea. Instead, I simply provide the flexibility to allow those who want todo this that ability. There are SetAssertionHandler
and GetAssertionHandler
functions which allow the programmer to specify an alternate assertion handling scheme. The only assumption Stroika makes about this scheme is that the assertion handling function not return (so it must either exit the program, or longjump/throw). Led makes no guarantee that attempts to throw out past an assertion will succeed.
GetAssertionHandler() never returns nullptr - it always returns some handler.
Definition at line 91 of file Assertions.cpp.
void Stroika::Foundation::Debug::SetAssertionHandler | ( | AssertionHandlerType | assertionHandler | ) |
See @'GetAssertionHandler'. If SetAssertionHandler() is called with nullptr, then this merely selects the default assertion handler.
Definition at line 111 of file Assertions.cpp.
void Stroika::Foundation::Debug::SetWeakAssertionHandler | ( | AssertionHandlerType | assertionHandler | ) |
See @'GetAssertionHandler'. If SetAssertionHandler() is called with nullptr, then this merely selects the default assertion handler.
Definition at line 165 of file Assertions.cpp.