1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #ifndef __cplusplus 17 #include <stddef.h> 18 #endif // __cplusplus 19 20 // Note: This file depends on the backend header already being included. 21 22 #include "pw_assert/options.h" 23 #include "pw_preprocessor/arguments.h" 24 #include "pw_preprocessor/compiler.h" 25 26 // PW_CRASH - Crash the system, with a message. 27 #define PW_CRASH PW_HANDLE_CRASH 28 29 // PW_CHECK - If condition evaluates to false, crash. Message optional. 30 #define PW_CHECK(condition, ...) \ 31 do { \ 32 if (!(condition)) { \ 33 _PW_CHECK_SELECT_MACRO( \ 34 #condition, PW_HAS_ARGS(__VA_ARGS__), __VA_ARGS__); \ 35 } \ 36 } while (0) 37 38 #define PW_DCHECK(...) \ 39 do { \ 40 if (PW_ASSERT_ENABLE_DEBUG) { \ 41 PW_CHECK(__VA_ARGS__); \ 42 } \ 43 } while (0) 44 45 // PW_D?CHECK_<type>_<comparison> macros - Binary comparison asserts. 46 // 47 // The below blocks are structured in table form, violating the 80-column 48 // Pigweed style, in order to make it clearer what is common and what isn't 49 // between the multitude of assert macro instantiations. To best view this 50 // section, turn off editor wrapping or make your editor wide. 51 // 52 // clang-format off 53 54 // Checks for int: LE, LT, GE, GT, EQ. 55 #define PW_CHECK_INT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, int, "%d", __VA_ARGS__) 56 #define PW_CHECK_INT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, int, "%d", __VA_ARGS__) 57 #define PW_CHECK_INT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, int, "%d", __VA_ARGS__) 58 #define PW_CHECK_INT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, int, "%d", __VA_ARGS__) 59 #define PW_CHECK_INT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, int, "%d", __VA_ARGS__) 60 #define PW_CHECK_INT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, int, "%d", __VA_ARGS__) 61 62 // Debug checks for int: LE, LT, GE, GT, EQ. 63 #define PW_DCHECK_INT_LE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_LE(__VA_ARGS__) 64 #define PW_DCHECK_INT_LT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_LT(__VA_ARGS__) 65 #define PW_DCHECK_INT_GE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_GE(__VA_ARGS__) 66 #define PW_DCHECK_INT_GT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_GT(__VA_ARGS__) 67 #define PW_DCHECK_INT_EQ(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_EQ(__VA_ARGS__) 68 #define PW_DCHECK_INT_NE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_NE(__VA_ARGS__) 69 70 // Checks for unsigned int: LE, LT, GE, GT, EQ. 71 #define PW_CHECK_UINT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, unsigned int, "%u", __VA_ARGS__) 72 #define PW_CHECK_UINT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, unsigned int, "%u", __VA_ARGS__) 73 #define PW_CHECK_UINT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, unsigned int, "%u", __VA_ARGS__) 74 #define PW_CHECK_UINT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, unsigned int, "%u", __VA_ARGS__) 75 #define PW_CHECK_UINT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, unsigned int, "%u", __VA_ARGS__) 76 #define PW_CHECK_UINT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, unsigned int, "%u", __VA_ARGS__) 77 78 // Debug checks for unsigned int: LE, LT, GE, GT, EQ. 79 #define PW_DCHECK_UINT_LE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_LE(__VA_ARGS__) 80 #define PW_DCHECK_UINT_LT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_LT(__VA_ARGS__) 81 #define PW_DCHECK_UINT_GE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_GE(__VA_ARGS__) 82 #define PW_DCHECK_UINT_GT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_GT(__VA_ARGS__) 83 #define PW_DCHECK_UINT_EQ(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_EQ(__VA_ARGS__) 84 #define PW_DCHECK_UINT_NE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_NE(__VA_ARGS__) 85 86 // Checks for pointer: LE, LT, GE, GT, EQ, NE. 87 #define PW_CHECK_PTR_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, const void*, "%p", __VA_ARGS__) 88 #define PW_CHECK_PTR_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, const void*, "%p", __VA_ARGS__) 89 #define PW_CHECK_PTR_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, const void*, "%p", __VA_ARGS__) 90 #define PW_CHECK_PTR_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, const void*, "%p", __VA_ARGS__) 91 #define PW_CHECK_PTR_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, const void*, "%p", __VA_ARGS__) 92 #define PW_CHECK_PTR_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, const void*, "%p", __VA_ARGS__) 93 94 // Check for pointer: NOTNULL. Use "nullptr" in C++, "NULL" in C. 95 #ifdef __cplusplus 96 #define PW_CHECK_NOTNULL(arga, ...) \ 97 _PW_CHECK_BINARY_CMP_IMPL(arga, !=, nullptr, const void*, "%p", __VA_ARGS__) 98 #else // __cplusplus 99 #define PW_CHECK_NOTNULL(arga, ...) \ 100 _PW_CHECK_BINARY_CMP_IMPL(arga, !=, NULL, const void*, "%p", __VA_ARGS__) 101 #endif // __cplusplus 102 103 // Debug checks for pointer: LE, LT, GE, GT, EQ, NE, and NOTNULL. 104 #define PW_DCHECK_PTR_LE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_LE(__VA_ARGS__) 105 #define PW_DCHECK_PTR_LT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_LT(__VA_ARGS__) 106 #define PW_DCHECK_PTR_GE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_GE(__VA_ARGS__) 107 #define PW_DCHECK_PTR_GT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_GT(__VA_ARGS__) 108 #define PW_DCHECK_PTR_EQ(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_EQ(__VA_ARGS__) 109 #define PW_DCHECK_PTR_NE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_NE(__VA_ARGS__) 110 #define PW_DCHECK_NOTNULL(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_NOTNULL(__VA_ARGS__) 111 112 // Checks for float: EXACT_LE, EXACT_LT, EXACT_GE, EXACT_GT, EXACT_EQ, EXACT_NE, 113 // NEAR. 114 #define PW_CHECK_FLOAT_NEAR(arga, argb, abs_tolerance, ...) \ 115 _PW_CHECK_FLOAT_NEAR(arga, argb, abs_tolerance, __VA_ARGS__) 116 #define PW_CHECK_FLOAT_EXACT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, float, "%f", __VA_ARGS__) 117 #define PW_CHECK_FLOAT_EXACT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, float, "%f", __VA_ARGS__) 118 #define PW_CHECK_FLOAT_EXACT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, float, "%f", __VA_ARGS__) 119 #define PW_CHECK_FLOAT_EXACT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, float, "%f", __VA_ARGS__) 120 #define PW_CHECK_FLOAT_EXACT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, float, "%f", __VA_ARGS__) 121 #define PW_CHECK_FLOAT_EXACT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, float, "%f", __VA_ARGS__) 122 123 // Debug checks for float: NEAR, EXACT_LE, EXACT_LT, EXACT_GE, EXACT_GT, 124 // EXACT_EQ. 125 #define PW_DCHECK_FLOAT_NEAR(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_NEAR(__VA_ARGS__) 126 #define PW_DCHECK_FLOAT_EXACT_LE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_LE(__VA_ARGS__) 127 #define PW_DCHECK_FLOAT_EXACT_LT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_LT(__VA_ARGS__) 128 #define PW_DCHECK_FLOAT_EXACT_GE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_GE(__VA_ARGS__) 129 #define PW_DCHECK_FLOAT_EXACT_GT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_GT(__VA_ARGS__) 130 #define PW_DCHECK_FLOAT_EXACT_EQ(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_EQ(__VA_ARGS__) 131 #define PW_DCHECK_FLOAT_EXACT_NE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_NE(__VA_ARGS__) 132 133 // clang-format on 134 135 // PW_CHECK - If condition evaluates to false, crash. Message optional. 136 #define PW_CHECK_OK(status, ...) \ 137 do { \ 138 if (status != PW_STATUS_OK) { \ 139 _PW_CHECK_OK_SELECT_MACRO(#status, \ 140 pw_StatusString(status), \ 141 PW_HAS_ARGS(__VA_ARGS__), \ 142 __VA_ARGS__); \ 143 } \ 144 } while (0) 145 146 #define PW_DCHECK_OK(...) \ 147 if (!(PW_ASSERT_ENABLE_DEBUG)) { \ 148 } else \ 149 PW_CHECK_OK(__VA_ARGS__) 150 151 // ========================================================================= 152 // Implementation for PW_CHECK 153 154 // Two layers of select macros are used to enable the preprocessor to expand 155 // macros in the arguments to ultimately token paste the final macro name based 156 // on whether there are printf-style arguments. 157 #define _PW_CHECK_SELECT_MACRO(condition, has_args, ...) \ 158 _PW_CHECK_SELECT_MACRO_EXPANDED(condition, has_args, __VA_ARGS__) 159 160 // Delegate to the macro 161 #define _PW_CHECK_SELECT_MACRO_EXPANDED(condition, has_args, ...) \ 162 _PW_CHECK_HAS_MSG_##has_args(condition, __VA_ARGS__) 163 164 // PW_CHECK version 1: No message or args 165 #define _PW_CHECK_HAS_MSG_0(condition, ignored_arg) \ 166 PW_HANDLE_ASSERT_FAILURE(condition, "") 167 168 // PW_CHECK version 2: With message (and maybe args) 169 #define _PW_CHECK_HAS_MSG_1(condition, ...) \ 170 PW_HANDLE_ASSERT_FAILURE(condition, __VA_ARGS__) 171 172 // ========================================================================= 173 // Implementation for PW_CHECK_<type>_<comparison> 174 175 // Two layers of select macros are used to enable the preprocessor to expand 176 // macros in the arguments to ultimately token paste the final macro name based 177 // on whether there are printf-style arguments. 178 #define _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(argument_a_str, \ 179 argument_a_val, \ 180 comparison_op_str, \ 181 argument_b_str, \ 182 argument_b_val, \ 183 type_fmt, \ 184 has_args, \ 185 ...) \ 186 _PW_CHECK_SELECT_BINARY_COMPARISON_MACRO_EXPANDED(argument_a_str, \ 187 argument_a_val, \ 188 comparison_op_str, \ 189 argument_b_str, \ 190 argument_b_val, \ 191 type_fmt, \ 192 has_args, \ 193 __VA_ARGS__) 194 195 // Delegate to the macro 196 #define _PW_CHECK_SELECT_BINARY_COMPARISON_MACRO_EXPANDED(argument_a_str, \ 197 argument_a_val, \ 198 comparison_op_str, \ 199 argument_b_str, \ 200 argument_b_val, \ 201 type_fmt, \ 202 has_args, \ 203 ...) \ 204 _PW_CHECK_BINARY_COMPARISON_HAS_MSG_##has_args(argument_a_str, \ 205 argument_a_val, \ 206 comparison_op_str, \ 207 argument_b_str, \ 208 argument_b_val, \ 209 type_fmt, \ 210 __VA_ARGS__) 211 212 // PW_CHECK_BINARY_COMPARISON version 1: No message or args 213 #define _PW_CHECK_BINARY_COMPARISON_HAS_MSG_0(argument_a_str, \ 214 argument_a_val, \ 215 comparison_op_str, \ 216 argument_b_str, \ 217 argument_b_val, \ 218 type_fmt, \ 219 ignored_arg) \ 220 PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(argument_a_str, \ 221 argument_a_val, \ 222 comparison_op_str, \ 223 argument_b_str, \ 224 argument_b_val, \ 225 type_fmt, \ 226 "") 227 228 // PW_CHECK_BINARY_COMPARISON version 2: With message (and maybe args) 229 #define _PW_CHECK_BINARY_COMPARISON_HAS_MSG_1(argument_a_str, \ 230 argument_a_val, \ 231 comparison_op_str, \ 232 argument_b_str, \ 233 argument_b_val, \ 234 type_fmt, \ 235 ...) \ 236 PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(argument_a_str, \ 237 argument_a_val, \ 238 comparison_op_str, \ 239 argument_b_str, \ 240 argument_b_val, \ 241 type_fmt, \ 242 __VA_ARGS__) 243 244 // For the binary assertions, this private macro is re-used for almost all of 245 // the variants. Due to limitations of C formatting, it is necessary to have 246 // separate macros for the types. 247 // 248 // The macro avoids evaluating the arguments multiple times at the cost of some 249 // macro complexity. 250 #define _PW_CHECK_BINARY_CMP_IMPL( \ 251 argument_a, comparison_op, argument_b, type_decl, type_fmt, ...) \ 252 do { \ 253 type_decl evaluated_argument_a = (type_decl)(argument_a); \ 254 type_decl evaluated_argument_b = (type_decl)(argument_b); \ 255 if (!(evaluated_argument_a comparison_op evaluated_argument_b)) { \ 256 _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(#argument_a, \ 257 evaluated_argument_a, \ 258 #comparison_op, \ 259 #argument_b, \ 260 evaluated_argument_b, \ 261 type_fmt, \ 262 PW_HAS_ARGS(__VA_ARGS__), \ 263 __VA_ARGS__); \ 264 } \ 265 } while (0) 266 267 // Custom implementation for FLOAT_NEAR which is implemented through two 268 // underlying checks which are not trivially replaced through the use of 269 // FLOAT_EXACT_LE & FLOAT_EXACT_GE. 270 #define _PW_CHECK_FLOAT_NEAR(argument_a, argument_b, abs_tolerance, ...) \ 271 do { \ 272 PW_CHECK_FLOAT_EXACT_GE(abs_tolerance, 0.0f); \ 273 float evaluated_argument_a = (float)(argument_a); \ 274 float evaluated_argument_b_min = (float)(argument_b)-abs_tolerance; \ 275 float evaluated_argument_b_max = (float)(argument_b) + abs_tolerance; \ 276 if (!(evaluated_argument_a >= evaluated_argument_b_min)) { \ 277 _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(#argument_a, \ 278 evaluated_argument_a, \ 279 ">=", \ 280 #argument_b " - abs_tolerance", \ 281 evaluated_argument_b_min, \ 282 "%f", \ 283 PW_HAS_ARGS(__VA_ARGS__), \ 284 __VA_ARGS__); \ 285 } else if (!(evaluated_argument_a <= evaluated_argument_b_max)) { \ 286 _PW_CHECK_BINARY_COMPARISON_SELECT_MACRO(#argument_a, \ 287 evaluated_argument_a, \ 288 "<=", \ 289 #argument_b " + abs_tolerance", \ 290 evaluated_argument_b_max, \ 291 "%f", \ 292 PW_HAS_ARGS(__VA_ARGS__), \ 293 __VA_ARGS__); \ 294 } \ 295 } while (0) 296 297 // ========================================================================= 298 // Implementation for PW_CHECK_OK 299 300 // Two layers of select macros are used to enable the preprocessor to expand 301 // macros in the arguments to ultimately token paste the final macro name based 302 // on whether there are printf-style arguments. 303 #define _PW_CHECK_OK_SELECT_MACRO( \ 304 status_expr_str, status_value_str, has_args, ...) \ 305 _PW_CHECK_OK_SELECT_MACRO_EXPANDED( \ 306 status_expr_str, status_value_str, has_args, __VA_ARGS__) 307 308 // Delegate to the macro 309 #define _PW_CHECK_OK_SELECT_MACRO_EXPANDED( \ 310 status_expr_str, status_value_str, has_args, ...) \ 311 _PW_CHECK_OK_HAS_MSG_##has_args( \ 312 status_expr_str, status_value_str, __VA_ARGS__) 313 314 // PW_CHECK_OK version 1: No message or args 315 #define _PW_CHECK_OK_HAS_MSG_0(status_expr_str, status_value_str, ignored_arg) \ 316 PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE( \ 317 status_expr_str, status_value_str, "==", "OkStatus()", "OK", "%s", "") 318 319 // PW_CHECK_OK version 2: With message (and maybe args) 320 #define _PW_CHECK_OK_HAS_MSG_1(status_expr_str, status_value_str, ...) \ 321 PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(status_expr_str, \ 322 status_value_str, \ 323 "==", \ 324 "OkStatus()", \ 325 "OK", \ 326 "%s", \ 327 __VA_ARGS__) 328