• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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