• 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 #ifdef __cplusplus
17 #include <type_traits>
18 #else
19 #include <stddef.h>
20 #endif  // __cplusplus
21 
22 // Note: This file depends on the backend header already being included.
23 
24 #include "pw_assert/config.h"
25 #include "pw_preprocessor/compiler.h"
26 
27 // PW_CRASH - Crash the system, with a message.
28 #define PW_CRASH PW_HANDLE_CRASH
29 
30 // PW_CHECK - If condition evaluates to false, crash. Message optional.
31 #define PW_CHECK(condition, ...)                            \
32   do {                                                      \
33     if (!(condition)) {                                     \
34       PW_HANDLE_ASSERT_FAILURE(#condition, "" __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_OK - If condition does not evaluate to PW_STATUS_OK, crash. Message
136 // optional.
137 #define PW_CHECK_OK(expression, ...)                                     \
138   do {                                                                   \
139     const _PW_CHECK_OK_STATUS _pw_assert_check_ok_status = (expression); \
140     if (_pw_assert_check_ok_status != PW_STATUS_OK) {                    \
141       _PW_CHECK_BINARY_ARG_HANDLER(                                      \
142           #expression,                                                   \
143           pw_StatusString(_pw_assert_check_ok_status),                   \
144           "==",                                                          \
145           "OkStatus()",                                                  \
146           "OK",                                                          \
147           "%s",                                                          \
148           "" __VA_ARGS__);                                               \
149     }                                                                    \
150   } while (0)
151 
152 #ifdef __cplusplus
153 #define _PW_CHECK_OK_STATUS ::pw::Status
154 #else
155 #define _PW_CHECK_OK_STATUS pw_Status
156 #endif  // __cplusplus
157 
158 #define PW_DCHECK_OK(...)          \
159   if (!(PW_ASSERT_ENABLE_DEBUG)) { \
160   } else                           \
161     PW_CHECK_OK(__VA_ARGS__)
162 
163 // Use a static_cast in C++ to avoid accidental comparisons between e.g. an
164 // integer and the CHECK message const char*.
165 #if defined(__cplusplus) && __cplusplus >= 201703L
166 
167 namespace pw::assert::internal {
168 
169 template <typename T, typename U>
ConvertToType(U * value)170 constexpr const void* ConvertToType(U* value) {
171   if constexpr (std::is_function<U>()) {
172     return reinterpret_cast<const void*>(value);
173   } else {
174     return static_cast<const void*>(value);
175   }
176 }
177 
178 template <typename T, typename U>
ConvertToType(const U & value)179 constexpr T ConvertToType(const U& value) {
180   return static_cast<T>(value);
181 }
182 
183 }  // namespace pw::assert::internal
184 
185 #define _PW_CHECK_CONVERT(type, name, arg) \
186   type name = ::pw::assert::internal::ConvertToType<type>(arg)
187 #else
188 #define _PW_CHECK_CONVERT(type, name, arg) type name = (type)(arg)
189 #endif  // __cplusplus
190 
191 // For the binary assertions, this private macro is re-used for almost all of
192 // the variants. Due to limitations of C formatting, it is necessary to have
193 // separate macros for the types.
194 //
195 // The macro avoids evaluating the arguments multiple times at the cost of some
196 // macro complexity.
197 #define _PW_CHECK_BINARY_CMP_IMPL(                                    \
198     arg_a, comparison_op, arg_b, type_decl, type_fmt, ...)            \
199   do {                                                                \
200     _PW_CHECK_CONVERT(type_decl, evaluated_argument_a, arg_a);        \
201     _PW_CHECK_CONVERT(type_decl, evaluated_argument_b, arg_b);        \
202     if (!(evaluated_argument_a comparison_op evaluated_argument_b)) { \
203       _PW_CHECK_BINARY_ARG_HANDLER(#arg_a,                            \
204                                    evaluated_argument_a,              \
205                                    #comparison_op,                    \
206                                    #arg_b,                            \
207                                    evaluated_argument_b,              \
208                                    type_fmt,                          \
209                                    "" __VA_ARGS__);                   \
210     }                                                                 \
211   } while (0)
212 
213 // All binary comparison CHECK macros are directed to this handler before
214 // hitting the CHECK backend. This controls whether evaluated values are
215 // captured.
216 #if PW_ASSERT_CAPTURE_VALUES
217 #define _PW_CHECK_BINARY_ARG_HANDLER(arg_a_str,              \
218                                      arg_a_val,              \
219                                      comparison_op_str,      \
220                                      arg_b_str,              \
221                                      arg_b_val,              \
222                                      type_fmt,               \
223                                      ...)                    \
224   PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(arg_a_str,         \
225                                           arg_a_val,         \
226                                           comparison_op_str, \
227                                           arg_b_str,         \
228                                           arg_b_val,         \
229                                           type_fmt,          \
230                                           __VA_ARGS__)
231 #else
232 #define _PW_CHECK_BINARY_ARG_HANDLER(arg_a_str,                           \
233                                      arg_a_val,                           \
234                                      comparison_op_str,                   \
235                                      arg_b_str,                           \
236                                      arg_b_val,                           \
237                                      type_fmt,                            \
238                                      ...)                                 \
239   PW_HANDLE_ASSERT_FAILURE(arg_a_str " " comparison_op_str " " arg_b_str, \
240                            __VA_ARGS__)
241 #endif  // PW_ASSERT_CAPTURE_VALUES
242 
243 // Custom implementation for FLOAT_NEAR which is implemented through two
244 // underlying checks which are not trivially replaced through the use of
245 // FLOAT_EXACT_LE & FLOAT_EXACT_GE.
246 #define _PW_CHECK_FLOAT_NEAR(argument_a, argument_b, abs_tolerance, ...)  \
247   do {                                                                    \
248     PW_CHECK_FLOAT_EXACT_GE(abs_tolerance, 0.0f);                         \
249     float evaluated_argument_a = (float)(argument_a);                     \
250     float evaluated_argument_b_min = (float)(argument_b)-abs_tolerance;   \
251     float evaluated_argument_b_max = (float)(argument_b) + abs_tolerance; \
252     if (!(evaluated_argument_a >= evaluated_argument_b_min)) {            \
253       _PW_CHECK_BINARY_ARG_HANDLER(#argument_a,                           \
254                                    evaluated_argument_a,                  \
255                                    ">=",                                  \
256                                    #argument_b " - abs_tolerance",        \
257                                    evaluated_argument_b_min,              \
258                                    "%f",                                  \
259                                    "" __VA_ARGS__);                       \
260     } else if (!(evaluated_argument_a <= evaluated_argument_b_max)) {     \
261       _PW_CHECK_BINARY_ARG_HANDLER(#argument_a,                           \
262                                    evaluated_argument_a,                  \
263                                    "<=",                                  \
264                                    #argument_b " + abs_tolerance",        \
265                                    evaluated_argument_b_max,              \
266                                    "%f",                                  \
267                                    "" __VA_ARGS__);                       \
268     }                                                                     \
269   } while (0)
270