Нагородить макросы вокруг assert() для выборочности нетрудно, было
бы желание. Нынче ИИ это делает легко и просто.
ส็็็็็็็็็็็็็็็็็็็็็็็็็༼ ຈل͜ຈ༽ส้้้้้้้้้้้้้้้้้้้้้้้
если что, я не проверял на предмет глюков
/* debug_assert.h — модифицированный assert() для C99 с несколькими уровнями
*
* Уровень проверок задаётся при компиляции:
* gcc -DASSERT_LEVEL=2 ...
*
* ASSERT_LEVEL = 0 → все ASSERT() полностью удаляются (как NDEBUG)
* ASSERT_LEVEL = 1 → работают только ASSERT(1, ...)
* ASSERT_LEVEL = 2 → работают ASSERT(1, ...) и ASSERT(2, ...)
* ASSERT_LEVEL = 3 → работают уровни 1-3
* и т.д. (можно добавить сколько угодно уровней)
*
* Преимущества:
* • Полностью compile-time отключение — код и вычисление выражений исчезают.
* • Нет runtime-if даже в отладке (компилятор видит только нужные макросы).
* • Совместимо со стандартным assert() (можно использовать параллельно).
* • Красивое сообщение с уровнем, функцией, файлом и строкой.
*/
#ifndef DEBUG_ASSERT_H
#define DEBUG_ASSERT_H
#include <stdio.h>
#include <stdlib.h>
/* По умолчанию — release-режим (всё выключено) */
#ifndef ASSERT_LEVEL
# define ASSERT_LEVEL 0
#endif
/* Если кто-то определил NDEBUG — принудительно выключаем всё */
#ifdef NDEBUG
# undef ASSERT_LEVEL
# define ASSERT_LEVEL 0
#endif
/* ================================================================ */
/* Вспомогательные макросы для генерации ASSERT_1, ASSERT_2, ... */
/* ================================================================ */
#define ASSERT_CAT(x, y) x ## y
#define ASSERT_CAT2(x, y) ASSERT_CAT(x, y)
/* Макрос, который будет расширяться в ASSERT_1, ASSERT_2 и т.д. */
#define ASSERT(lvl, expr) ASSERT_CAT2(ASSERT_, lvl)(expr)
/* ================================================================ */
/* Генерация каждого уровня (добавляйте новые уровни сюда) */
/* ================================================================ */
#if ASSERT_LEVEL >= 1
# define ASSERT_1(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, \
"ASSERT(1) failed: %s\n" \
" function: %s\n" \
" file: %s:%d\n", \
#expr, __func__, __FILE__, __LINE__); \
abort(); \
} \
} while (0)
#else
# define ASSERT_1(expr) ((void)0)
#endif
#if ASSERT_LEVEL >= 2
# define ASSERT_2(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, \
"ASSERT(2) failed: %s\n" \
" function: %s\n" \
" file: %s:%d\n", \
#expr, __func__, __FILE__, __LINE__); \
abort(); \
} \
} while (0)
#else
# define ASSERT_2(expr) ((void)0)
#endif
#if ASSERT_LEVEL >= 3
# define ASSERT_3(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, \
"ASSERT(3) failed: %s\n" \
" function: %s\n" \
" file: %s:%d\n", \
#expr, __func__, __FILE__, __LINE__); \
abort(); \
} \
} while (0)
#else
# define ASSERT_3(expr) ((void)0)
#endif
#if ASSERT_LEVEL >= 4
# define ASSERT_4(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, \
"ASSERT(4) failed: %s\n" \
" function: %s\n" \
" file: %s:%d\n", \
#expr, __func__, __FILE__, __LINE__); \
abort(); \
} \
} while (0)
#else
# define ASSERT_4(expr) ((void)0)
#endif
/* Добавляйте сюда ASSERT_5, ASSERT_6 … сколько нужно */
/* ================================================================ */
/* Удобные алиасы (по желанию) */
/* ================================================================ */
#define ASSERT_CRITICAL(expr) ASSERT(1, expr) /* всегда включено при ASSERT_LEVEL >= 1 */
#define ASSERT_NORMAL(expr) ASSERT(2, expr)
#define ASSERT_HEAVY(expr) ASSERT(3, expr) /* тяжёлые проверки */
#define ASSERT_PARANOID(expr) ASSERT(4, expr) /* параноидальные проверки */
/* Если хочется использовать старый assert() как ASSERT(1, ...) */
#if ASSERT_LEVEL >= 1
# undef assert
# define assert(expr) ASSERT(1, expr)
#else
# undef assert
# define assert(expr) ((void)0)
#endif
#endif /* DEBUG_ASSERT_H */
/////////////////////////////////////////////////////
#include "debug_assert.h"
void example(int *ptr, size_t n)
{
ASSERT(1, ptr != NULL); // критическая проверка
ASSERT(2, n < 1000); // обычная
ASSERT(3, is_valid_data(ptr, n)); // тяжёлая (включается только при -DASSERT_LEVEL=3)
ASSERT_PARANOID(n % 8 == 0); // совсем параноидальная
// Можно и классический assert, если хочется
assert(ptr != NULL);
}