Assert.h#

Fully qualified name: carb/Assert.h

File members: carb/Assert.h

// SPDX-FileCopyrightText: Copyright (c) 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.

#pragma once

#include "assert/IAssert.h"

#include <exception> // for std::terminate

#ifdef DOXYGEN_BUILD
#    define CARB_ASSERT_ENABLED CARB_DEBUG
#    define CARB_CHECK_ENABLED 1
#    define CARB_RELEASE_ASSERT_ENABLED 1
#    define CARB_FATAL_UNLESS_ENABLED 1
#    define CARB_ASSERT(cond, ...) ((void)0)
#    define CARB_CHECK(cond, ...) ((void)0)
#    define CARB_RELEASE_ASSERT(cond, ...) ((void)0)
#    define CARB_FATAL_UNLESS(cond, fmt, ...) (!(cond) ? (std::terminate(), false) : true)
#else
/* main assertion test entry point.  This is implemented as a single conditional statement to
 * ensure that the assertion failure breakpoint occurs on the same line of code as the assertion
 * test itself. CARB_CHECK() exists in release and debug, and CARB_ASSERT() is debug-only.
 */
// example-begin CARB_IMPL_ASSERT
#    define CARB_IMPL_ASSERT(cond, type, ...)                                                                          \
        (CARB_LIKELY(cond) ||                                                                                          \
         ![&](const char* funcname__, ...) CARB_NOINLINE {                                                             \
             return ::carb::assert::detail::dispatchAssert(                                                            \
                 ::carb::assert::AssertEntry{ (type), #cond, funcname__, __FILE__, __LINE__ }, ##__VA_ARGS__);         \
         }(CARB_PRETTY_FUNCTION) ||                                                                                    \
         (CARB_BREAK_POINT(), false))

// Use as:
// #define CARB_ASSERT(cond, ...) CARB_IMPL_ASSERT(cond, ::carb::assert::Type::eAssert, ##__VA_ARGS__)
// #define CARB_CHECK(cond, ...) CARB_IMPL_ASSERT(cond, ::carb::assert::Type::eCheck, ##__VA_ARGS__)
// #define CARB_RELEASE_ASSERT(cond, ...) CARB_IMPL_ASSERT(cond, ::carb::assert::Type::eReleaseAssert, ##__VA_ARGS__)

// NOTE: Ideal implementation is below, but this requires C++23 for static in constexpr functions where it might be used
#    if 0
#        define CARB_IMPL_ASSERT(cond, type, ...)                                                                      \
            do                                                                                                         \
            {                                                                                                          \
                CARB_LIKELY_IF(cond) break;                                                                            \
                constexpr static ::carb::assert::AssertEntry implAssertEntry{ (type), #cond, CARB_PRETTY_FUNCTION,     \
                                                                              __FILE__, __LINE__ };                    \
                if (::carb::assert::detail::dispatchAssert(&implAssertEntry, ##__VA_ARGS__))                           \
                    CARB_BREAK_POINT();                                                                                \
            } while (false)
#    endif
// example-end CARB_IMPL_ASSERT

#    ifndef CARB_ASSERT
#        ifndef CARB_ASSERT_ENABLED
#            define CARB_ASSERT_ENABLED CARB_DEBUG
#        endif
#        if CARB_ASSERT_ENABLED
#            define CARB_ASSERT(cond, ...) CARB_IMPL_ASSERT(cond, ::carb::assert::Type::eAssert, ##__VA_ARGS__)
#        else
#            define CARB_ASSERT(cond, ...) ((void)0)
#        endif
#    else
// CARB_ASSERT was already defined
#        ifndef CARB_ASSERT_ENABLED
#            define CARB_ASSERT /* cause an error showing where it was already defined */
#            error CARB_ASSERT_ENABLED must also be defined if CARB_ASSERT is pre-defined!
#        endif
#    endif
#endif

#ifndef CARB_CHECK
#    ifndef CARB_CHECK_ENABLED
#        define CARB_CHECK_ENABLED 1
#    endif
#    if CARB_CHECK_ENABLED
#        define CARB_CHECK(cond, ...) CARB_IMPL_ASSERT(cond, ::carb::assert::Type::eCheck, ##__VA_ARGS__)
#    else
#        define CARB_CHECK(...) ((void)0)
#    endif
#else
// CARB_CHECK was already defined
#    ifndef CARB_CHECK_ENABLED
#        define CARB_CHECK /* cause an error showing where it was already defined */
#        error CARB_CHECK_ENABLED must also be defined if CARB_CHECK is pre-defined!
#    endif
#endif

#ifndef CARB_RELEASE_ASSERT
#    ifndef CARB_RELEASE_ASSERT_ENABLED
#        define CARB_RELEASE_ASSERT_ENABLED 1
#    endif
#    if CARB_RELEASE_ASSERT_ENABLED
#        define CARB_RELEASE_ASSERT(cond, ...)                                                                         \
            CARB_IMPL_ASSERT(cond, ::carb::assert::Type::eReleaseAssert, ##__VA_ARGS__)
#    else
#        define CARB_RELEASE_ASSERT(...) ((void)0)
#    endif
#else
// CARB_RELEASE_ASSERT was already defined
#    ifndef CARB_RELEASE_ASSERT_ENABLED
#        define CARB_RELEASE_ASSERT /* cause an error showing where it was already defined */
#        error CARB_RELEASE_ASSERT_ENABLED must also be defined if CARB_RELEASE_ASSERT is pre-defined!
#    endif
#endif

#define CARB_FATAL_UNLESS_ENABLED 1
#ifndef CARB_FATAL_UNLESS
// example-begin CARB_FATAL_UNLESS
#    define CARB_FATAL_UNLESS(cond, fmt, ...)                                                                          \
        (CARB_LIKELY(cond) ||                                                                                          \
            ([&](const char* funcname__, ...) CARB_NOINLINE {                                                          \
                if (false)                                                                                             \
                    ::printf(fmt, ##__VA_ARGS__);                                                                      \
                ::carb::assert::detail::dispatchAssert(::carb::assert::AssertEntry{ ::carb::assert::Type::eFatal,      \
                    #cond, funcname__, __FILE__, __LINE__ }, fmt, ##__VA_ARGS__);                                      \
             }(CARB_PRETTY_FUNCTION), std::terminate(), false))
// example-end CARB_FATAL_UNLESS
#endif

#define CARB_UNIMPLEMENTED(msg, ...)                                                                                   \
    do                                                                                                                 \
    {                                                                                                                  \
        CARB_FATAL_UNLESS(false, (msg), ##__VA_ARGS__);                                                                \
        std::terminate();                                                                                              \
    } while (0)

#define CARB_MACOS_UNIMPLEMENTED() CARB_UNIMPLEMENTED("Unimplemented on Mac OS")

#define CARB_ASSERT_STRUCTS_MATCH(A, B)                                                                                \
    static_assert(                                                                                                     \
        sizeof(A) == sizeof(B) && alignof(A) == alignof(B), "Size or alignment mismatch between " #A " and " #B ".")

#define CARB_ASSERT_MEMBERS_MATCH(A, a, B, b)                                                                          \
    static_assert(offsetof(A, a) == offsetof(B, b) && sizeof(A::a) == sizeof(B::b),                                    \
                  "Offset or size mismatch between members " #a " of " #A " and " #b " of " #B ".")