@@ -152,3 +152,147 @@ static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining
152
152
#endif
153
153
154
154
#endif
155
+
156
+ #ifndef ARRAYSIZE2_H
157
+ #define ARRAYSIZE2_H
158
+
159
+ #ifndef __has_feature
160
+ #define __has_feature (x ) 0 // Compatibility with non-clang compilers.
161
+ #endif
162
+
163
+ #if __cplusplus >= 199711L
164
+ #pragma message "using Ivan J. Johnson's ARRAY_SIZE2"
165
+
166
+ // Works on older compilers, even Visual C++ 6....
167
+ // Created by Ivan J. Johnson, March 06, 2007
168
+ // See http://drdobbs.com/cpp/197800525?pgno=1
169
+ //
170
+ // Pseudocode:
171
+ // if x is not an array
172
+ // issue a compile-time error
173
+ // else
174
+ // use the traditional (non-typesafe) C99 COUNTOF expression
175
+ //
176
+ // If the argument is any of:
177
+ // object of class type, such as an std::vector
178
+ // floating-point type
179
+ // function pointer
180
+ // pointer-to-member
181
+ // then the first reinterpret_cast<> is not legal (compiler error)
182
+ //
183
+ // The type for check1 is chosen and named to help understand
184
+ // the cause of the error, because the class name is likely to
185
+ // appear in the compiler error message.
186
+ //
187
+ // If check1 succeeds, then the argument must be one of:
188
+ // an integral type
189
+ // an enumerated type
190
+ // a pointer to an object
191
+ // an array
192
+ //
193
+ // Check2 expands approximately to sizeof(check_type(x, &x)),
194
+ // where check_type is an overloaded function.
195
+ // Because this is purely a compile-time computation,
196
+ // the function is never really called or even implemented,
197
+ // but it lets the compiler apply overload resolution,
198
+ // which allows further type discrimination.
199
+ // There are three possibilities to consider:
200
+ // x is an integral type or enumerated type.
201
+ // In this case, neither of the two function overloads
202
+ // is a match, resulting in a compiler error.
203
+ // x is a pointer to an object.
204
+ // In this case, the first argument to check_type()
205
+ // is a pointer and the second one is a pointer-to-pointer.
206
+ // The best function match is the first overload of check_type,
207
+ // the one that returns an incomplete type (Is_pointer).
208
+ // However, because Is_pointer is an incomplete type,
209
+ // sizeof(Is_pointer) is not a valid expression,
210
+ // resulting in a compiler error.
211
+ // x is an array.
212
+ // In this case, the first argument to check_type()
213
+ // is an array and the second is a pointer-to-array.
214
+ // A pointer-to-array is *NOT* convertible to a
215
+ // pointer-to-pointer, so the first overload of
216
+ // check_type() is not a match.
217
+ // However, an array IS convertible to a pointer,
218
+ // and a pointer-to-array already is a pointer.
219
+ // Any pointer is convertible to a void*,
220
+ // so the second function overload is a match.
221
+ // That overload returns a complete type (Is_array).
222
+ // Because it's a complete type,
223
+ // sizeof(Is_array) is a valid expression.
224
+ // Thus, the compiler has EXCLUDED every possible type
225
+ // except arrays via compilation errors before reaching
226
+ // the third line.
227
+ // Moreover, check1 and check2 are reduced to the value zero,
228
+ // while the third line is the old type-unsafe C-style macro,
229
+ // now made entirely type-safe.
230
+ //
231
+ // Additional benefits:
232
+ // The result is itself constexpr
233
+ //
234
+ //
235
+ #define ARRAY_SIZE2 (arr ) ( \
236
+ 0 * sizeof (reinterpret_cast <const ::Bad_arg_to_COUNTOF*>(arr)) + /* check1*/ \
237
+ 0 * sizeof (::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + /* check2*/ \
238
+ sizeof (arr) / sizeof ((arr)[0 ]) /* eval */ \
239
+ )
240
+
241
+ struct Bad_arg_to_COUNTOF {
242
+ class Is_pointer ; // incomplete
243
+ class Is_array {};
244
+ template <typename T>
245
+ static Is_pointer check_type (const T*, const T* const *);
246
+ static Is_array check_type (const void *, const void *);
247
+ };
248
+
249
+ #elif __cplusplus >= 201103L || /* any compiler claiming C++11 support */ \
250
+ _MSC_VER >= 1900 || /* Visual C++ 2015 or higher */ \
251
+ __has_feature (cxx_constexpr) /* CLang versions supporting constexp */
252
+
253
+ #pragma message " C++11 version ARRAY_SIZE2"
254
+
255
+ namespace detail
256
+ {
257
+ template <typename T, std::size_t N>
258
+ constexpr std::size_t countof (T const (&)[N]) noexcept
259
+ {
260
+ return N;
261
+ }
262
+ } // namespace detail
263
+ #define ARRAY_SIZE2 (arr ) detail::countof(arr)
264
+
265
+ #elif _MSC_VER // Visual C++ fallback
266
+
267
+ #pragma message "using Microsoft Visual C++ intrinsic ARRAY_SIZE2"
268
+ #define ARRAY_SIZE2 (arr ) _countof(arr)
269
+
270
+ #elif __cplusplus >= 199711L && ( /* C++ 98 trick */ \
271
+ defined (__INTEL_COMPILER) || \
272
+ defined(__clang__) || \
273
+ (defined(__GNUC__) && ( \
274
+ (__GNUC__ > 4 ) || \
275
+ (__GNUC__ == 4 && __GNUC_MINOR__ >= 4 ) \
276
+ )))
277
+
278
+ #pragma message " C++98 version ARRAY_SIZE2"
279
+
280
+ template <typename T, std::size_t N>
281
+ char (&_ArraySizeHelperRequiresArray (T(&)[N]))[N];
282
+ #define ARRAY_SIZE2 (x ) sizeof (_ArraySizeHelperRequiresArray(x))
283
+
284
+ #else
285
+
286
+ #pragma message "Using type-unsafe version of ARRAY_SIZE2"
287
+ // This is the worst-case scenario macro.
288
+ // While it is valid C, it is NOT typesafe.
289
+ // For example, if the parameter arr is a pointer instead of array,
290
+ // the compiler will SILENTLY give a (likely) incorrect result.
291
+ #define ARRAY_SIZE2 (arr ) sizeof (arr) / sizeof (arr[0 ])
292
+
293
+ #endif
294
+
295
+
296
+ #endif // ARRAYSIZE2_H
297
+
298
+
0 commit comments