├── .gitignore ├── test_package ├── CMakeLists.txt ├── conanfile.py └── example.cpp ├── LICENSE ├── conanfile.py ├── example.cpp ├── .clang-format ├── doc └── README-template.md ├── CMakeLists.txt ├── README.md └── debug_assert.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pyc 3 | conanbuildinfo.cmake 4 | -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(debug_assert_test_package) 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 5 | conan_basic_setup(TARGETS) 6 | 7 | add_executable(example example.cpp) 8 | if(NOT MSVC) 9 | target_compile_options(example PRIVATE -std=c++11) 10 | endif() 11 | 12 | target_link_libraries(example PRIVATE ${CONAN_TARGETS}) 13 | -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | import os 3 | 4 | class DebugAssertTest(ConanFile): 5 | settings = 'os', 'compiler', 'build_type', 'arch' 6 | generators = 'cmake' 7 | 8 | def build(self): 9 | cmake = CMake(self) 10 | cmake.configure(defs={'CMAKE_VERBOSE_MAKEFILE': 'ON'}) 11 | cmake.build() 12 | 13 | def test(self): 14 | self.run(os.path.join('.', 'bin', 'example')) 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016-2018 Jonathan Müller 2 | 3 | This software is provided 'as-is', without any express or 4 | implied warranty. In no event will the authors be held 5 | liable for any damages arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute 9 | it freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; 12 | you must not claim that you wrote the original software. 13 | If you use this software in a product, an acknowledgment 14 | in the product documentation would be appreciated but 15 | is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, 18 | and must not be misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any 21 | source distribution. 22 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile 2 | from conans.tools import download, untargz 3 | import os 4 | 5 | 6 | class DebugAssert(ConanFile): 7 | name = 'debug_assert' 8 | url = 'https://foonathan.github.io/blog/2016/09/16/assertions.html' 9 | version = '1.3.4' 10 | exports = '*.hpp' 11 | generators = 'cmake' 12 | 13 | 14 | def source(self): 15 | tar = 'debug_assert-{}.tar.gz'.format(self.version) 16 | url = 'https://github.com/foonathan/debug_assert/archive/v{}.tar.gz'.format(self.version) 17 | 18 | download(url, tar) 19 | untargz(tar) 20 | 21 | def package(self): 22 | srcdir = 'debug_assert-{}'.format(self.version) 23 | 24 | # The header is packaged twice: At include/ (for unqualified #include 25 | # directives) and include/debug_assert/ 26 | self.copy('debug_assert.hpp', src=srcdir, dst=os.path.join('include', 'debug_assert')) 27 | self.copy('debug_assert.hpp', src=srcdir, dst='include') 28 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | #include "debug_assert.hpp" 2 | 3 | #include 4 | 5 | //=== module A ===// 6 | #define MODULE_A_LEVEL 1 // macro to control assertion level 7 | // usually set by the build system 8 | 9 | // tag type that defines a module 10 | struct module_a : debug_assert::default_handler, // it uses the default handler 11 | debug_assert::set_level // and this level 12 | { 13 | }; 14 | 15 | void module_a_func(void* ptr) 16 | { 17 | DEBUG_ASSERT(ptr, module_a{}); // minimal assertion 18 | DEBUG_ASSERT(2 + 2 == 4, module_a{}, debug_assert::level<2>{}); // assertion with level 19 | DEBUG_ASSERT(1 == 0, module_a{}, 20 | "this should be true"); // assertion with additional parameters, i.e. a message 21 | DEBUG_UNREACHABLE(module_a{}); // mark unreachable statements 22 | } 23 | 24 | //=== module B ===// 25 | #define MODULE_B_LEVEL 2 26 | 27 | struct module_b : debug_assert::set_level // b uses all assertions with level <= 2 28 | { 29 | // module b uses a different handler 30 | // it does not support a message 31 | // instead you can specify a pointer value 32 | static void handle(const debug_assert::source_location& loc, const char* expression, 33 | void* ptr = nullptr) noexcept 34 | { 35 | std::cerr << "Assertion failure '" << loc.file_name << ':' << loc.line_number << ": " 36 | << expression; 37 | if (ptr) 38 | std::cerr << " - pointer is " << ptr; 39 | std::cerr << '\n'; 40 | } 41 | }; 42 | 43 | void module_b_func(int& value, void* ptr) 44 | { 45 | DEBUG_ASSERT(ptr == &value, module_b{}, ptr); // uses the additional custom parameter 46 | DEBUG_ASSERT(ptr == &value, module_b{}, debug_assert::level<2>{}, 47 | ptr); // also works with a custom level 48 | } 49 | 50 | int main() 51 | { 52 | module_a_func(nullptr); 53 | int val = 5; 54 | module_b_func(val, &val); 55 | } 56 | -------------------------------------------------------------------------------- /test_package/example.cpp: -------------------------------------------------------------------------------- 1 | #include "debug_assert.hpp" 2 | 3 | #include 4 | #include 5 | 6 | //=== module A ===// 7 | #define MODULE_A_LEVEL 1 // macro to control assertion level 8 | // usually set by the build system 9 | 10 | // tag type that defines a module 11 | struct module_a 12 | : debug_assert::default_handler, // it uses the default handler 13 | debug_assert::set_level // and this level 14 | {}; 15 | 16 | void module_a_func(void* ptr) 17 | { 18 | DEBUG_ASSERT(ptr, module_a{}); // minimal assertion 19 | DEBUG_ASSERT(2 + 2 == 4, module_a{}, debug_assert::level<2>{}); // assertion with level 20 | DEBUG_ASSERT(1 == 0, module_a{}, "this should be true"); // assertion with additional parameters, i.e. a message 21 | DEBUG_UNREACHABLE(module_a{}); // mark unreachable statements 22 | } 23 | 24 | //=== module B ===// 25 | #define MODULE_B_LEVEL 2 26 | 27 | struct module_b 28 | : debug_assert::set_level// b uses all assertions with level <= 2 29 | { 30 | // module b uses a different handler 31 | // it does not support a message 32 | // instead you can specify a pointer value 33 | static void handle(const debug_assert::source_location& loc, const char* expression, void* ptr = nullptr) 34 | { 35 | std::cerr << "Assertion failure '" << loc.file_name << ':' << loc.line_number << ": " << expression; 36 | if (ptr) 37 | std::cerr << " - pointer is " << ptr; 38 | std::cerr << '\n'; 39 | } 40 | }; 41 | 42 | void module_b_func(int &value, void* ptr) 43 | { 44 | DEBUG_ASSERT(ptr == &value, module_b{}, ptr); // uses the additional custom parameter 45 | DEBUG_ASSERT(ptr == &value, module_b{}, debug_assert::level<2>{}, ptr); // also works with a custom level 46 | } 47 | 48 | int main() 49 | { 50 | auto old_handler = std::signal(SIGABRT, +[](int signal) 51 | { 52 | if(signal == SIGABRT) 53 | { 54 | std::cerr << "Please never call std::abort() in production :)"; 55 | std::exit(EXIT_SUCCESS); 56 | } 57 | }); 58 | 59 | if(old_handler == SIG_ERR) 60 | { 61 | std::cerr << "Error settings custom SIGABRT handler"; 62 | return EXIT_FAILURE; 63 | } 64 | 65 | module_a_func(nullptr); 66 | int val = 5; 67 | module_b_func(val, &val); 68 | } 69 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | AlignAfterOpenBracket: Align 3 | AlignConsecutiveAssignments: true 4 | AlignConsecutiveDeclarations: true 5 | AlignEscapedNewlinesLeft: Right 6 | AlignOperands: true 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: false 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortCaseLabelsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: Empty 12 | AllowShortIfStatementsOnASingleLine: false 13 | AllowShortLoopsOnASingleLine: false 14 | AlwaysBreakAfterReturnType: None 15 | AlwaysBreakBeforeMultilineStrings: false 16 | AlwaysBreakTemplateDeclarations: true 17 | BinPackArguments: true 18 | BinPackParameters: true 19 | BreakBeforeBraces: Custom 20 | BraceWrapping: 21 | AfterClass: true 22 | AfterControlStatement: true 23 | AfterEnum: true 24 | AfterFunction: true 25 | AfterNamespace: true 26 | AfterStruct: true 27 | AfterUnion: true 28 | AfterExternBlock: true 29 | BeforeCatch: true 30 | BeforeElse: true 31 | SplitEmptyFunction: false 32 | SplitEmptyRecord: false 33 | SplitEmptyNamespace: false 34 | BreakBeforeBinaryOperators: All 35 | BreakBeforeTernaryOperators: true 36 | BreakConstructorInitializers: BeforeColon 37 | BreakStringLiterals: false 38 | ColumnLimit: 100 39 | CompactNamespaces: true 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 41 | ConstructorInitializerIndentWidth: 0 42 | ContinuationIndentWidth: 4 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: false 45 | FixNamespaceComments: true 46 | IncludeBlocks: Preserve 47 | IndentCaseLabels: false 48 | IndentPPDirectives: AfterHash 49 | IndentWidth: 4 50 | IndentWrappedFunctionNames: true 51 | KeepEmptyLinesAtTheStartOfBlocks: false 52 | Language: Cpp 53 | MaxEmptyLinesToKeep: 1 54 | NamespaceIndentation: Inner 55 | PenaltyBreakBeforeFirstCallParameter: 19937 56 | PenaltyReturnTypeOnItsOwnLine: 19937 57 | PointerAlignment: Left 58 | ReflowComments: true 59 | SortIncludes: true 60 | SortUsingDeclarations: true 61 | SpaceAfterCStyleCast: false 62 | SpaceAfterTemplateKeyword: true 63 | SpaceBeforeAssignmentOperators: true 64 | SpaceBeforeParens: ControlStatements 65 | SpaceInEmptyParentheses: false 66 | SpacesBeforeTrailingComments: 1 67 | SpacesInAngles: false 68 | SpacesInCStyleCastParentheses: false 69 | SpacesInParentheses: false 70 | SpacesInSquareBrackets: false 71 | Standard: Cpp11 72 | TabWidth: 4 73 | UseTab: Never 74 | -------------------------------------------------------------------------------- /doc/README-template.md: -------------------------------------------------------------------------------- 1 | # debug_assert 2 | 3 | ![Project Status](https://img.shields.io/endpoint?url=https%3A%2F%2Fwww.jonathanmueller.dev%2Fproject%2Fdebug_assert%2Findex.json) 4 | [![Download](https://api.bintray.com/packages/manu343726/conan-packages/debug_assert%3AManu343726/images/download.svg) ](https://bintray.com/manu343726/conan-packages/debug_assert%3AManu343726/_latestVersion) 5 | 6 | debug_assert is a simple, C++11, header-only library that provides a very flexible `DEBUG_ASSERT()` macro. 7 | How many times did you write an assertion macro yourself, because `assert()` is controlled globally and cannot be enabled for certain parts of the program only? 8 | This library solves the problem by providing a flexible, modular assertion macro. 9 | 10 | ## Features 11 | 12 | * No dependencies. It only requires `std::abort()` and - unless `DEBUG_ASSERT_NO_STDIO` is defined - `std::fprintf()`. 13 | * Single, small header file that just needs to be copied into your own project. 14 | * Customizable assertion handling - assertion failure will call a user-defined function, with user-defined arguments. 15 | * Modular - enable or disable assertions for different parts of the same program. 16 | * Support for levels - give levels to your assertion macro and only enable certain levels of assertions. 17 | * Little preprocessor use - just a single assertion macro which is needed to get the stringified expression and source location. Enabling/Disabling is controlled by compile time programming instead of preprocessor conditionals. 18 | * Fast - even though a disabled assertion will still expand to something, 19 | there is no overhead with even basic optimizations enabled and very little without optimization (just the code to read `__FILE__` and `__LINE__`). To be precise: It will only evaluate the assertion expression if the assertion is enabled! 20 | 21 | ## Overview 22 | 23 | The basic usage of the library is like so: 24 | 25 | ```cpp 26 | DEBUG_ASSERT(1 + 1 == 2, my_module{}); // basic 27 | DEBUG_ASSERT(1 + 1 == 2, my_module{}, debug_assert::level<2>{}); // with level 28 | ``` 29 | 30 | Where `my_module` is a user-defined tag type that will both control the assertion level and the handler code. 31 | It looks like this: 32 | 33 | ```cpp 34 | struct my_module 35 | : debug_assert::default_handler, // use the default handler 36 | debug_assert::set_level<-1> // level -1, i.e. all assertions, 0 would mean none, 1 would be level 1, 2 level 2 or lower,... 37 | {}; 38 | ``` 39 | 40 | A module handler must have `static` function `handle()` that takes a `debug_assert::source_location`, the stringified expression and any additional arguments you pass to `DEBUG_ASSERT()` (besides the `debug_assert::level`). 41 | 42 | See `example.cpp` for more information and [read the blogpost](https://foonathan.github.io/blog/2016/09/16/assertions.html). 43 | 44 | ### CMake 45 | 46 | For convenience you can also use CMake to setup the include directory and have options that map to the customizable macros. 47 | Simple call `add_subdirectory(path/to/debug_assert)` and then `target_link_libraries(my_target PUBLIC debug_assert)`. 48 | It will not actually build something, only setup the flags. 49 | Note that it will not enable C++11 support. 50 | The options are named like the macros. 51 | 52 | ## Documentation 53 | 54 | > Generated by [standardese](https://github.com/foonathan/standardese). 55 | 56 | {{ standardese_doc $file commonmark }} 57 | 58 | ## Acknowledgements 59 | 60 | Thanks a lot to [@Manu343726](https://github.com/Manu343726), [@verri](https://github.com/verri) and [@pfultz2](https://github.com/pfultz2). 61 | 62 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2018 Jonathan Müller 2 | # This file is subject to the license terms in the LICENSE file 3 | # found in the top-level directory of this distribution. 4 | 5 | cmake_minimum_required(VERSION 3.5) 6 | project(DEBUG_ASSERT VERSION 1.3.4) 7 | 8 | # Determine if debug_assert is built as a subproject (using add_subdirectory) 9 | # or if it is the master project. 10 | set(MASTER_PROJECT OFF) 11 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 12 | set(MASTER_PROJECT ON) 13 | endif () 14 | 15 | # options 16 | option(DEBUG_ASSERT_NO_STDIO "whether or not the default_handler uses fprintf() to print an error message" OFF) 17 | if(DEBUG_ASSERT_NO_STDIO) 18 | set(_debug_assert_no_stdio DEBUG_ASSERT_NO_STDIO) 19 | endif() 20 | 21 | option(DEBUG_ASSERT_DISABLE "completely disable assertion macro" OFF) 22 | if(DEBUG_ASSERT_DISABLE) 23 | set(_debug_assert_disable DEBUG_ASSERT_DISABLE) 24 | endif() 25 | 26 | set(DEBUG_ASSERT_MARK_UNREACHABLE "" CACHE STRING "override of DEBUG_ASSERT_MARK_UNREACHABLE") 27 | if(DEBUG_ASSERT_MARK_UNREACHABLE) 28 | set(_debug_assert_mark_unreachable "DEBUG_ASSERT_MARK_UNREACHABLE=${DEBUG_ASSERT_MARK_UNREACHABLE}") 29 | endif() 30 | 31 | set(DEBUG_ASSERT_ASSUME "" CACHE STRING "override of DEBUG_ASSERT_ASSUME macro") 32 | if(DEBUG_ASSERT_ASSUME) 33 | set(_debug_assert_assume "DEBUG_ASSERT_ASSUME=${DEBUG_ASSERT_ASSUME}") 34 | endif() 35 | 36 | set(DEBUG_ASSERT_FORCE_INLINE "" CACHE STRING "override of DEBUG_ASSERT_FORCE_INLINE macro") 37 | if(DEBUG_ASSERT_FORCE_INLINE) 38 | set(_debug_assert_force_inline "DEBUG_ASSERT_FORCE_INLINE=${DEBUG_ASSERT_FORCE_INLINE}") 39 | endif() 40 | 41 | option(DEBUG_ASSERT_INSTALL "Generate the install target." ${MASTER_PROJECT}) 42 | 43 | # interface target 44 | add_library(debug_assert INTERFACE) 45 | target_sources(debug_assert INTERFACE $) 46 | target_include_directories(debug_assert INTERFACE $) 47 | target_include_directories(debug_assert SYSTEM INTERFACE $/include>) 48 | target_compile_definitions(debug_assert INTERFACE 49 | ${_debug_assert_no_stdio} 50 | ${_debug_assert_disable} 51 | ${_debug_assert_mark_unreachable} 52 | ${_debug_assert_assume} 53 | ${_debug_assert_force_inline}) 54 | 55 | # example target 56 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 57 | add_executable(debug_assert_example EXCLUDE_FROM_ALL example.cpp) 58 | target_link_libraries(debug_assert_example PUBLIC debug_assert) 59 | endif() 60 | 61 | # Create package config files 62 | include( CMakePackageConfigHelpers ) 63 | set(CONFIG_PACKAGE_INSTALL_DIR lib/cmake/debug_assert) 64 | 65 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/debug_assert-config.cmake " 66 | include(\${CMAKE_CURRENT_LIST_DIR}/debug_assert-targets.cmake) 67 | set(debug_assert_LIBRARY debug_assert) 68 | set(debug_assert_LIBRARIES debug_assert) 69 | ") 70 | 71 | set(DEBUG_ASSERT_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) 72 | set(CMAKE_SIZEOF_VOID_P "") 73 | write_basic_package_version_file( 74 | ${CMAKE_CURRENT_BINARY_DIR}/debug_assert-config-version.cmake 75 | VERSION 1.3.4 76 | COMPATIBILITY SameMajorVersion 77 | ) 78 | set(CMAKE_SIZEOF_VOID_P ${DEBUG_ASSERT_CMAKE_SIZEOF_VOID_P}) 79 | 80 | if(DEBUG_ASSERT_INSTALL) 81 | # Install target and header 82 | install(TARGETS debug_assert 83 | EXPORT debug_assert-targets 84 | DESTINATION lib) 85 | 86 | install(FILES debug_assert.hpp DESTINATION include) 87 | 88 | install( EXPORT debug_assert-targets 89 | DESTINATION 90 | ${CONFIG_PACKAGE_INSTALL_DIR} 91 | ) 92 | 93 | install( FILES 94 | ${CMAKE_CURRENT_BINARY_DIR}/debug_assert-config.cmake 95 | ${CMAKE_CURRENT_BINARY_DIR}/debug_assert-config-version.cmake 96 | DESTINATION 97 | ${CONFIG_PACKAGE_INSTALL_DIR} ) 98 | endif() 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # debug_assert 2 | 3 | ![Project Status](https://img.shields.io/endpoint?url=https%3A%2F%2Fwww.jonathanmueller.dev%2Fproject%2Fdebug_assert%2Findex.json) 4 | [![Download](https://api.bintray.com/packages/manu343726/conan-packages/debug_assert%3AManu343726/images/download.svg) ](https://bintray.com/manu343726/conan-packages/debug_assert%3AManu343726/_latestVersion) 5 | 6 | debug_assert is a simple, C++11, header-only library that provides a very flexible `DEBUG_ASSERT()` macro. 7 | How many times did you write an assertion macro yourself, because `assert()` is controlled globally and cannot be enabled for certain parts of the program only? 8 | This library solves the problem by providing a flexible, modular assertion macro. 9 | 10 | ## Features 11 | 12 | * No dependencies. It only requires `std::abort()` and - unless `DEBUG_ASSERT_NO_STDIO` is defined - `std::fprintf()`. 13 | * Single, small header file that just needs to be copied into your own project. 14 | * Customizable assertion handling - assertion failure will call a user-defined function, with user-defined arguments. 15 | * Modular - enable or disable assertions for different parts of the same program. 16 | * Support for levels - give levels to your assertion macro and only enable certain levels of assertions. 17 | * Little preprocessor use - just a single assertion macro which is needed to get the stringified expression and source location. Enabling/Disabling is controlled by compile time programming instead of preprocessor conditionals. 18 | * Fast - even though a disabled assertion will still expand to something, 19 | there is no overhead with even basic optimizations enabled and very little without optimization (just the code to read `__FILE__` and `__LINE__`). To be precise: It will only evaluate the assertion expression if the assertion is enabled! 20 | 21 | ## Overview 22 | 23 | The basic usage of the library is like so: 24 | 25 | ```cpp 26 | DEBUG_ASSERT(1 + 1 == 2, my_module{}); // basic 27 | DEBUG_ASSERT(1 + 1 == 2, my_module{}, debug_assert::level<2>{}); // with level 28 | ``` 29 | 30 | Where `my_module` is a user-defined tag type that will both control the assertion level and the handler code. 31 | It looks like this: 32 | 33 | ```cpp 34 | struct my_module 35 | : debug_assert::default_handler, // use the default handler 36 | debug_assert::set_level<-1> // level -1, i.e. all assertions, 0 would mean none, 1 would be level 1, 2 level 2 or lower,... 37 | {}; 38 | ``` 39 | 40 | A module handler must have `static` function `handle()` that takes a `debug_assert::source_location`, the stringified expression and any additional arguments you pass to `DEBUG_ASSERT()` (besides the `debug_assert::level`). 41 | 42 | See `example.cpp` for more information and [read the blogpost](https://foonathan.github.io/blog/2016/09/16/assertions.html). 43 | 44 | ### CMake 45 | 46 | For convenience you can also use CMake to setup the include directory and have options that map to the customizable macros. 47 | Simple call `add_subdirectory(path/to/debug_assert)` and then `target_link_libraries(my_target PUBLIC debug_assert)`. 48 | It will not actually build something, only setup the flags. 49 | Note that it will not enable C++11 support. 50 | The options are named like the macros. 51 | 52 | ## Documentation 53 | 54 | > Generated by [standardese](https://github.com/foonathan/standardese). 55 | 56 | # Header file `debug_assert.hpp` 57 | 58 |
#define DEBUG_ASSERT_MARK_UNREACHABLE
 59 | 
 60 | #define DEBUG_ASSERT_FORCE_INLINE
 61 | 
 62 | #define DEBUG_ASSERT_CUR_SOURCE_LOCATION
 63 | 
 64 | #define DEBUG_ASSERT(Expr, ...)
 65 | 
 66 | #define DEBUG_UNREACHABLE(...)
 67 | 
 68 | namespace debug_assert
 69 | {
 70 |     struct source_location;
 71 | 
 72 |     template <unsigned Level>
 73 |     struct level;
 74 | 
 75 |     template <unsigned Level>
 76 |     struct set_level;
 77 | 
 78 |     struct allow_exception;
 79 | 
 80 |     struct no_handler;
 81 | 
 82 |     struct default_handler;
 83 | }
84 | 85 | ## Macro `DEBUG_ASSERT_CUR_SOURCE_LOCATION` 86 | 87 |
#define DEBUG_ASSERT_CUR_SOURCE_LOCATION
88 | 89 | Expands to the current [debug\_assert::source\_location](doc_debug_assert.md#debug_assert::source_location). 90 | 91 | ## Macro `DEBUG_ASSERT` 92 | 93 |
#define DEBUG_ASSERT(Expr, ...)
94 | 95 | Usage: \`DEBUG\_ASSERT(\, \, \[\\], \[\\]. Where: 96 | 97 | - `` - the expression to check for, the expression `!` must be well-formed and contextually convertible to `bool`. 98 | - `` - an object of the module specific handler 99 | - `` (optional, defaults to `1`) - the level of the assertion, must be an object of type [debug\_assert::level\](doc_debug_assert.md#debug_assert::level-Level-). 100 | - `` (optional) - any additional arguments that are just forwarded to the handler function. 101 | 102 | It will only check the assertion if `` is less than or equal to `Handler::level`. A failed assertion will call: `Handler::handle(location, expression, args)`. `location` is the [debug\_assert::source\_location](doc_debug_assert.md#debug_assert::source_location) at the macro expansion, `expression` is the stringified expression and `args` are the `` as-is. If the handler function returns, it will call \[std::abort()\]. 103 | 104 | *Notes*: Define `DEBUG_ASSERT_DISABLE` to completely disable this macro, it will expand to nothing. This should not be necessary, the regular version is optimized away completely. 105 | 106 | ## Macro `DEBUG_UNREACHABLE` 107 | 108 |
#define DEBUG_UNREACHABLE(...)
109 | 110 | Marks a branch as unreachable. 111 | 112 | Usage: `DEBUG_UNREACHABLE(, [], [])` Where: 113 | 114 | - `` - an object of the module specific handler 115 | - `` (optional, defaults to `1`) - the level of the assertion, must be an object of type [debug\_assert::level\](doc_debug_assert.md#debug_assert::level-Level-). 116 | - `` (optional) - any additional arguments that are just forwarded to the handler function. 117 | 118 | It will only check the assertion if `` is less than or equal to `Handler::level`. A failed assertion will call: `Handler::handle(location, "", args)`. and `args` are the `` as-is. If the handler function returns, it will call \[std::abort()\]. 119 | 120 | *Notes*: Define `DEBUG_ASSERT_DISABLE` to completely disable this macro, it will expand to `DEBUG_ASSERT_MARK_UNREACHABLE`. This should not be necessary, the regular version is optimized away completely. 121 | 122 | ## Struct `debug_assert::source_location` 123 | 124 |
struct source_location
125 | {
126 |     const char* file_name;
127 | 
128 |     unsigned line_number;
129 | };
130 | 131 | Defines a location in the source code. 132 | 133 | **Members:** 134 | 135 | - `line_number` - \< The file name. \< The line number. 136 | 137 | ## Class template `debug_assert::level` 138 | 139 |
template <unsigned Level>
140 | struct level
141 | {
142 | };
143 | 144 | Tag type to indicate the level of an assertion. 145 | 146 | ## Class template `debug_assert::set_level` 147 | 148 |
template <unsigned Level>
149 | struct set_level
150 | {
151 |     static const unsigned level = Level;
152 | };
153 | 154 | Helper class that sets a certain level. Inherit from it in your module handler. 155 | 156 | ## Struct `debug_assert::allow_exception` 157 | 158 |
struct allow_exception
159 | {
160 |     static const bool throwing_exception_is_allowed = true;
161 | };
162 | 163 | Helper class that controls whether the handler can throw or not. Inherit from it in your module handler. If the module does not inherit from this class, it is assumed that the handle does not throw. 164 | 165 | ## Struct `debug_assert::no_handler` 166 | 167 |
struct no_handler
168 | {
169 |     template <typename ... Args>
170 |     static void handle(const source_location&, const char*, Args&&...) noexcept;
171 | };
172 | 173 | Does not do anything to handle a failed assertion (except calling [std::abort()](http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=std::abort())). Inherit from it in your module handler. 174 | 175 | ### Function template `debug_assert::no_handler::handle` 176 | 177 |
template <typename ... Args>
178 | static void handle(const source_location&, const char*, Args&&...) noexcept;
179 | 180 | *Effects*: Does nothing. 181 | 182 | *Notes*: Can take any additional arguments. 183 | 184 | ----- 185 | 186 | ## Struct `debug_assert::default_handler` 187 | 188 |
struct default_handler
189 | {
190 |     static void handle(const source_location& loc, const char* expression, const char* message = nullptr) noexcept;
191 | };
192 | 193 | The default handler that writes a message to `stderr`. Inherit from it in your module handler. 194 | 195 | ### Function `debug_assert::default_handler::handle` 196 | 197 |
static void handle(const source_location& loc, const char* expression, const char* message = nullptr) noexcept;
198 | 199 | *Effects*: Prints a message to `stderr`. 200 | 201 | *Notes*: It can optionally accept an additional message string. 202 | 203 | *Notes*: If `DEBUG_ASSERT_NO_STDIO` is defined, it will do nothing. 204 | 205 | ----- 206 | 207 | ----- 208 | 209 | 210 | ## Acknowledgements 211 | 212 | Thanks a lot to [@Manu343726](https://github.com/Manu343726), [@verri](https://github.com/verri) and [@pfultz2](https://github.com/pfultz2). 213 | 214 | -------------------------------------------------------------------------------- /debug_assert.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================// 2 | // Copyright (C) 2016-2018 Jonathan Müller 3 | // 4 | // This software is provided 'as-is', without any express or 5 | // implied warranty. In no event will the authors be held 6 | // liable for any damages arising from the use of this software. 7 | // 8 | // Permission is granted to anyone to use this software for any purpose, 9 | // including commercial applications, and to alter it and redistribute 10 | // it freely, subject to the following restrictions: 11 | // 12 | // 1. The origin of this software must not be misrepresented; 13 | // you must not claim that you wrote the original software. 14 | // If you use this software in a product, an acknowledgment 15 | // in the product documentation would be appreciated but 16 | // is not required. 17 | // 18 | // 2. Altered source versions must be plainly marked as such, 19 | // and must not be misrepresented as being the original software. 20 | // 21 | // 3. This notice may not be removed or altered from any 22 | // source distribution. 23 | //======================================================================// 24 | 25 | #ifndef DEBUG_ASSERT_HPP_INCLUDED 26 | #define DEBUG_ASSERT_HPP_INCLUDED 27 | 28 | #include 29 | 30 | #ifndef DEBUG_ASSERT_NO_STDIO 31 | # include 32 | #endif 33 | 34 | #ifndef DEBUG_ASSERT_MARK_UNREACHABLE 35 | # ifdef __GNUC__ 36 | # define DEBUG_ASSERT_MARK_UNREACHABLE __builtin_unreachable() 37 | # elif defined(_MSC_VER) 38 | # define DEBUG_ASSERT_MARK_UNREACHABLE __assume(0) 39 | # else 40 | /// Hint to the compiler that a code branch is unreachable. 41 | /// Define it yourself prior to including the header to override it. 42 | /// \notes This must be usable in an expression. 43 | # define DEBUG_ASSERT_MARK_UNREACHABLE 44 | # endif 45 | #endif 46 | 47 | #ifndef DEBUG_ASSERT_FORCE_INLINE 48 | # ifdef __GNUC__ 49 | # define DEBUG_ASSERT_FORCE_INLINE [[gnu::always_inline]] inline 50 | # elif defined(_MSC_VER) 51 | # define DEBUG_ASSERT_FORCE_INLINE __forceinline 52 | # else 53 | /// Strong hint to the compiler to inline a function. 54 | /// Define it yourself prior to including the header to override it. 55 | # define DEBUG_ASSERT_FORCE_INLINE inline 56 | # endif 57 | #endif 58 | 59 | namespace debug_assert 60 | { 61 | //=== source location ===// 62 | /// Defines a location in the source code. 63 | struct source_location 64 | { 65 | const char* file_name; ///< The file name. 66 | unsigned line_number; ///< The line number. 67 | }; 68 | 69 | /// Expands to the current [debug_assert::source_location](). 70 | #define DEBUG_ASSERT_CUR_SOURCE_LOCATION \ 71 | debug_assert::source_location \ 72 | { \ 73 | __FILE__, static_cast(__LINE__) \ 74 | } 75 | 76 | //=== level ===// 77 | /// Tag type to indicate the level of an assertion. 78 | template 79 | struct level 80 | {}; 81 | 82 | /// Helper class that sets a certain level. 83 | /// Inherit from it in your module handler. 84 | template 85 | struct set_level 86 | { 87 | static const unsigned level = Level; 88 | }; 89 | 90 | template 91 | const unsigned set_level::level; 92 | 93 | /// Helper class that controls whether the handler can throw or not. 94 | /// Inherit from it in your module handler. 95 | /// If the module does not inherit from this class, it is assumed that 96 | /// the handle does not throw. 97 | struct allow_exception 98 | { 99 | static const bool throwing_exception_is_allowed = true; 100 | }; 101 | 102 | //=== handler ===// 103 | /// Does not do anything to handle a failed assertion (except calling 104 | /// [std::abort()]()). 105 | /// Inherit from it in your module handler. 106 | struct no_handler 107 | { 108 | /// \effects Does nothing. 109 | /// \notes Can take any additional arguments. 110 | template 111 | static void handle(const source_location&, const char*, Args&&...) noexcept 112 | {} 113 | }; 114 | 115 | /// The default handler that writes a message to `stderr`. 116 | /// Inherit from it in your module handler. 117 | struct default_handler 118 | { 119 | /// \effects Prints a message to `stderr`. 120 | /// \notes It can optionally accept an additional message string. 121 | /// \notes If `DEBUG_ASSERT_NO_STDIO` is defined, it will do nothing. 122 | static void handle(const source_location& loc, const char* expression, 123 | const char* message = nullptr) noexcept 124 | { 125 | #ifndef DEBUG_ASSERT_NO_STDIO 126 | if (*expression == '\0') 127 | { 128 | if (message) 129 | ::fprintf(stderr, "[debug assert] %s:%u: Unreachable code reached - %s.\n", 130 | loc.file_name, loc.line_number, message); 131 | else 132 | ::fprintf(stderr, "[debug assert] %s:%u: Unreachable code reached.\n", 133 | loc.file_name, loc.line_number); 134 | } 135 | else if (message) 136 | ::fprintf(stderr, "[debug assert] %s:%u: Assertion '%s' failed - %s.\n", loc.file_name, 137 | loc.line_number, expression, message); 138 | else 139 | ::fprintf(stderr, "[debug assert] %s:%u: Assertion '%s' failed.\n", loc.file_name, 140 | loc.line_number, expression); 141 | #else 142 | (void)loc; 143 | (void)expression; 144 | (void)message; 145 | #endif 146 | } 147 | }; 148 | 149 | /// \exclude 150 | namespace detail 151 | { 152 | //=== boilerplate ===// 153 | // from http://en.cppreference.com/w/cpp/types/remove_reference 154 | template 155 | struct remove_reference 156 | { 157 | using type = T; 158 | }; 159 | 160 | template 161 | struct remove_reference 162 | { 163 | using type = T; 164 | }; 165 | 166 | template 167 | struct remove_reference 168 | { 169 | using type = T; 170 | }; 171 | 172 | // from http://stackoverflow.com/a/27501759 173 | template 174 | T&& forward(typename remove_reference::type& t) 175 | { 176 | return static_cast(t); 177 | } 178 | 179 | template 180 | T&& forward(typename remove_reference::type&& t) 181 | { 182 | return static_cast(t); 183 | } 184 | 185 | template 186 | struct enable_if; 187 | 188 | template 189 | struct enable_if 190 | { 191 | using type = T; 192 | }; 193 | 194 | template 195 | struct enable_if 196 | {}; 197 | 198 | //=== helper class to check if throw is allowed ===// 199 | template 200 | struct allows_exception 201 | { 202 | static const bool value = false; 203 | }; 204 | 205 | template 206 | struct allows_exception::type> 208 | { 209 | static const bool value = Handler::throwing_exception_is_allowed; 210 | }; 211 | 212 | //=== regular void fake ===// 213 | struct regular_void 214 | { 215 | constexpr regular_void() = default; 216 | 217 | // enable conversion to anything 218 | // conversion must not actually be used 219 | template 220 | constexpr operator T&() const noexcept 221 | { 222 | // doesn't matter how to get the T 223 | return DEBUG_ASSERT_MARK_UNREACHABLE, *static_cast(nullptr); 224 | } 225 | }; 226 | 227 | //=== assert implementation ===// 228 | // function name will be shown on constexpr assertion failure 229 | template 230 | regular_void debug_assertion_failed(const source_location& loc, const char* expression, 231 | Args&&... args) 232 | { 233 | #if defined(_MSC_VER) 234 | # pragma warning(push) 235 | # pragma warning(disable : 4702) 236 | #endif 237 | return Handler::handle(loc, expression, detail::forward(args)...), ::abort(), 238 | regular_void(); 239 | #if defined(_MSC_VER) 240 | # pragma warning(pop) 241 | #endif 242 | } 243 | 244 | // use enable if instead of tag dispatching 245 | // this removes on additional function and encourage optimization 246 | template 247 | constexpr auto do_assert( 248 | const Expr& expr, const source_location& loc, const char* expression, Handler, level, 249 | Args&&... args) noexcept(!allows_exception::value 250 | || noexcept(Handler::handle(loc, expression, 251 | detail::forward(args)...))) -> 252 | typename enable_if::type 253 | { 254 | static_assert(Level > 0, "level of an assertion must not be 0"); 255 | return expr() ? regular_void() 256 | : debug_assertion_failed(loc, expression, 257 | detail::forward(args)...); 258 | } 259 | 260 | template 261 | DEBUG_ASSERT_FORCE_INLINE constexpr auto do_assert(const Expr&, const source_location&, 262 | const char*, Handler, level, 263 | Args&&...) noexcept -> 264 | typename enable_if<(Level > Handler::level), regular_void>::type 265 | { 266 | return regular_void(); 267 | } 268 | 269 | template 270 | constexpr auto do_assert( 271 | const Expr& expr, const source_location& loc, const char* expression, Handler, 272 | Args&&... args) noexcept(!allows_exception::value 273 | || noexcept(Handler::handle(loc, expression, 274 | detail::forward(args)...))) -> 275 | typename enable_if::type 276 | { 277 | return expr() ? regular_void() 278 | : debug_assertion_failed(loc, expression, 279 | detail::forward(args)...); 280 | } 281 | 282 | template 283 | DEBUG_ASSERT_FORCE_INLINE constexpr auto do_assert(const Expr&, const source_location&, 284 | const char*, Handler, Args&&...) noexcept -> 285 | typename enable_if::type 286 | { 287 | return regular_void(); 288 | } 289 | 290 | constexpr bool always_false() noexcept 291 | { 292 | return false; 293 | } 294 | } // namespace detail 295 | } // namespace debug_assert 296 | 297 | //=== assertion macros ===// 298 | #ifndef DEBUG_ASSERT_DISABLE 299 | /// The assertion macro. 300 | // 301 | /// Usage: `DEBUG_ASSERT(, , [], 302 | /// []. 303 | /// Where: 304 | /// * `` - the expression to check for, the expression `!` must be 305 | /// well-formed and contextually convertible to `bool`. 306 | /// * `` - an object of the module specific handler 307 | /// * `` (optional, defaults to `1`) - the level of the assertion, must 308 | /// be an object of type [debug_assert::level](). 309 | /// * `` (optional) - any additional arguments that are 310 | /// just forwarded to the handler function. 311 | /// 312 | /// It will only check the assertion if `` is less than or equal to 313 | /// `Handler::level`. 314 | /// A failed assertion will call: `Handler::handle(location, expression, args)`. 315 | /// `location` is the [debug_assert::source_location]() at the macro expansion, 316 | /// `expression` is the stringified expression and `args` are the 317 | /// `` as-is. 318 | /// If the handler function returns, it will call [std::abort()]. 319 | /// 320 | /// The macro will expand to a `void` expression. 321 | /// 322 | /// \notes Define `DEBUG_ASSERT_DISABLE` to completely disable this macro, it 323 | /// will expand to nothing. 324 | /// This should not be necessary, the regular version is optimized away 325 | /// completely. 326 | # define DEBUG_ASSERT(Expr, ...) \ 327 | static_cast(debug_assert::detail::do_assert([&]() noexcept { return Expr; }, \ 328 | DEBUG_ASSERT_CUR_SOURCE_LOCATION, #Expr, \ 329 | __VA_ARGS__)) 330 | 331 | /// Marks a branch as unreachable. 332 | /// 333 | /// Usage: `DEBUG_UNREACHABLE(, [], [])` 334 | /// Where: 335 | /// * `` - an object of the module specific handler 336 | /// * `` (optional, defaults to `1`) - the level of the assertion, must 337 | /// be an object of type [debug_assert::level](). 338 | /// * `` (optional) - any additional arguments that are 339 | /// just forwarded to the handler function. 340 | /// 341 | /// It will only check the assertion if `` is less than or equal to 342 | /// `Handler::level`. 343 | /// A failed assertion will call: `Handler::handle(location, "", args)`. 344 | /// and `args` are the `` as-is. 345 | /// If the handler function returns, it will call [std::abort()]. 346 | /// 347 | /// This macro is also usable in a constant expression, 348 | /// i.e. you can use it in a `constexpr` function to verify a condition like so: 349 | /// `cond(val) ? do_sth(val) : DEBUG_UNREACHABLE(…)`. 350 | /// You can't use `DEBUG_ASSERT` there. 351 | /// 352 | /// The macro will expand to an expression convertible to any type, 353 | /// although the resulting object is invalid, 354 | /// which doesn't matter, as the statement is unreachable anyway. 355 | /// 356 | /// \notes Define `DEBUG_ASSERT_DISABLE` to completely disable this macro, it 357 | /// will expand to `DEBUG_ASSERT_MARK_UNREACHABLE`. 358 | /// This should not be necessary, the regular version is optimized away 359 | /// completely. 360 | # define DEBUG_UNREACHABLE(...) \ 361 | debug_assert::detail::do_assert(debug_assert::detail::always_false, \ 362 | DEBUG_ASSERT_CUR_SOURCE_LOCATION, "", __VA_ARGS__) 363 | #else 364 | # define DEBUG_ASSERT(Expr, ...) static_cast(0) 365 | 366 | # define DEBUG_UNREACHABLE(...) \ 367 | (DEBUG_ASSERT_MARK_UNREACHABLE, debug_assert::detail::regular_void()) 368 | #endif 369 | 370 | #endif // DEBUG_ASSERT_HPP_INCLUDED 371 | --------------------------------------------------------------------------------