
/*
 * SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
 * Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: LicenseRef-NvidiaProprietary
 *
 * NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
 * property and proprietary rights in and to this material, related
 * documentation and any modifications thereto. Any use, reproduction,
 * disclosure or distribution of this material and related documentation
 * without an express license agreement from NVIDIA CORPORATION or
 * its affiliates is strictly prohibited.
 */

/** Asserts
 * this file contains assert macros for asserting pre/post/invariant conditions.
 * on error, you can: return/break/continue/throw.
 * assertions violations are bugs. we log them, an abort the computation.
 */

#pragma once

#include "config.h"

//! DEV-ENV ONLY:
//!  1. by definining AM_ASSERT_LOG_ERROR,
//!     you can use your own error logger to not be coupled to the logger.
#ifndef AM_ASSERT_LOG_ERROR
#define AM_ASSERT_LOG_ERROR ERROR
#include "am_log.h"
#endif
//!  2. by building with './configure --enable-debug --enable-debugger-asserts' this macro will be defined.
//!     we use C's assert() to break into the debugger, when assertion fails.
//!     default is no-op as we don't want debug builds to terminate on assert (as C's assert() does)
#ifdef AM_ASSERT_USE_C_ASSERT
#include <cassert>
#define AM_ASSERT_C_ASSERT assert
#else
#define AM_ASSERT_C_ASSERT(...)
#endif

//! assert a condition, on failure return an error value
#define AM_ASSERT(cond, error_value, fmt, arg...) AM_ASSERT_IMPL(cond, return error_value, fmt, ##arg)

//! assert a condition, on failure return (use when a function return void)
#define AM_ASSERT_V(cond, fmt, arg...) AM_ASSERT_IMPL(cond, return, fmt, ##arg)

//! assert a condition, on failure throw an exception
#define AM_ASSERT_THROW(cond, fmt, arg...) AM_ASSERT_IMPL(cond, (am_assert_do_throw(fmt, ##arg)), fmt, ##arg)

//! assert a condition, on failure do a 'continue' (in a loop)
#define AM_ASSERT_CONTINUE(cond, fmt, arg...) AM_ASSERT_IMPL(cond, continue, fmt, ##arg)

//! assert a condition, on failure do a 'break' (in a loop/switch)
#define AM_ASSERT_BREAK(cond, fmt, arg...) AM_ASSERT_IMPL(cond, break, fmt, ##arg)

//! assert a condition, on failure do nothing
#define AM_ASSERT_NOOP(cond, fmt, arg...) AM_ASSERT_IMPL(cond, ((void)0), fmt, ##arg)

//! the assert's implementation
//! prefer the use of one of the above macros,
//! as they give you a structured assertion handling
#define AM_ASSERT_IMPL(cond, on_error_exit, fmt, ...)                             \
    if (!(cond)) {                                                                \
        AM_ASSERT_LOG_ERROR("-A- assert(" #cond ") failed. " fmt, ##__VA_ARGS__); \
        AM_ASSERT_C_ASSERT(cond && " with message: " fmt #__VA_ARGS__);           \
        on_error_exit;                                                            \
    }

void am_assert_do_throw(const char* format, ...);
