// File: dbc.h // SPDX-License-Identifier: Unlicensed // // This is free and unencumbered software released into the public domain. /*! * \file dbc.h * * \brief This header file defines macros that implement the * Design By Contract (DBC) protocol. * * The macro functions \c DBC_REQUIRE, \c DBC_ENSURE, and \c DBC_INVARIANT * are defined. * * If the macro \c DBC_ALL has been defined before this header file is * included, or none of the macros \c DBC_DISABLE, \c DBC_ENABLE_REQUIRE, * \c DBC_ENABLE_ENSURE, or \c DBC_ENABLE_INVARIANT have been defined then all * of the DBC-check macro functions perform the specified checks and report * error if appropriate. * * If only certain macro functions are required to perform their function then * the required macro functions can be enabled by defining the appropriate * \c DBC_ENABLE_REQUIRE, \c DBC_ENABLE_ENSURE, and/or DBC_ENABLE_INVARIANT * macros before including the \c dbc.h header file. * * \version 1.0 * \date 2023 */ #ifndef DBC_H_ #define DBC_H_ #ifndef DBC_DISABLE #include // for fprintf, stderr #include // for _Bool, true, false #include // for exit, EXIT_FAILURE /*! * \internal * * \def DBC_ALL * * \brief If this macro is defined then all of the \c DBC macros will be * defined. * * If none of the macros \c DBC_DISABLE, \c DBC_ENABLE_REQUIRE, * \c DBC_ENABLE_ENSURE, or \c DBC_ENABLE_INVARIANT have been defined then * all the DBC-check macros are implicitly defined. * * \endinternal */ #if !( defined( DBC_ALL ) || defined( DBC_ENABLE_REQUIRE ) \ || defined( DBC_ENABLE_ENSURE ) || defined( DBC_ENABLE_INVARIANT ) ) // If none of the macros that control whether or not DBC // checks are enabled are defined then define them all #define DBC_ALL #define DBC_ENABLE_REQUIRE #define DBC_ENABLE_ENSURE #define DBC_ENABLE_INVARIANT #endif /*! * \def DBC_REQUIRE( pred ) * * \brief This macro function is used to ensure that conditions are met at * the beginning of a function call. * * If the \c DBC_ENABLE_REQUIRE macro is defined then the \c DBC_REQUIRE macro * is defined, otherwise this macro does nothing. * * \param [in] pred The predicate that is to be tested to ensure that it * is true. */ #ifndef DBC_REQUIRE #ifdef DBC_ENABLE_REQUIRE #define DBC_REQUIRE( pred ) DBC_ASSERT( pred, "Precondition", #pred, \ __func__, __FILE__, __LINE__ ) #else // if DBC_REQUIRE is not defined #define DBC_REQUIRE( ... ) #endif #else // if DBC_REQUIRE is already defined #error "DBC_REQUIRE already defined" #endif /*! * \def DBC_ENSURE( pred ) * * \brief This macro function is used to ensure that conditions are met at * the end of a function call. * * If the \c DBC_ENABLE_ENSURE macro is defined then the \c DBC_ENSURE * macro is defined, otherwise this macro does nothing. * * \param [in] pred The predicate that is to be tested to ensure that it * is true. */ #ifndef DBC_ENSURE #ifdef DBC_ENABLE_ENSURE #define DBC_ENSURE( pred ) DBC_ASSERT( pred, "Postcondition", #pred, \ __func__, __FILE__, __LINE__ ) #else // if DBC_ENSURE is not defined #define DBC_ENSURE( pred ) #endif #else // if DBC_ENSURE is already defined #error "DBC_ENSURE already defined" #endif /*! * \def DBC_INVARIANT( pred ) * * \brief This macro function is used to ensure that conditions are met at * any point in the programs execution. * * If the \c DBC_ENABLE_INVARIANT macro is defined then the \c DBC_INVARIANT * macro is defined, otherwise this macro does nothing. * * \param [in] pred The predicate that is to be tested to ensure that it * is true. */ #ifndef DBC_INVARIANT #if defined( DBC_ENABLE_INVARIANT ) #define DBC_INVARIANT( pred ) DBC_ASSERT( pred, "Invariant", #pred, \ __func__, __FILE__, __LINE__ ) #else // if DBC_INVARIANT is not defined #define DBC_INVARIANT( pred ) #endif #else // if DBC_INVARIANT is already defined #error "DBC_INVARIANT already defined" #endif /*! * \internal * * \def DBC_ASSERT( PRED, DBC_DESC, PRED_STR, FN_NAME, FILENAME, LINE ) * * \brief This function is used to ensure that a condition is met. * * This macro function is used by all of the \c DBC-check macros. * * If the \p pred is not met then an error message is written to \c stderr and * the program is exited with exit code \c EXIT_FAILURE. * * \param [in] pred The predicate that is to be tested to ensure that * it is true. * \param [in] dbc_desc The type of DBC assertion (i.e. require, ensure, * or invariant). This parameter is used in the * construction of the assertion failure message that * is written to \c stderr if \p pred is not true. * \param [in] pred_str A string representation of the \p pred that is * being tested. * \param [in] fn_name The name of the function where the \p pred is * being tested. * \param [in] filename The name of the file where the \p fn_name resides. * \param [in] line The line number of the file where the test is * occurring. * * \see DBC_REQUIRE * \see DBC_ENSURE * \see DBC_INVARIANT * * \endinternal */ #ifndef DBC_ASSERT #define DBC_ASSERT( pred, dbc_desc, pred_str, fn_name, filename, line ) \ do { \ if ( !( pred ) ) { \ fprintf( stderr, \ "%s: Assertion failed: Error " \ "%s: <%s> file <%s> line %d.\n", \ dbc_desc, pred_str, fn_name, filename, line ); \ exit( EXIT_FAILURE ); \ } \ } while (0) #else // if DBC_ASSERT is already defined #error "DBC_ASSERT already defined" #endif #else // if DBC_DISABLE is defined // Define the DBC_REQUIRE, DBC_ENSURE, DBC_INVARIANT, and DBC_ASSERT macros, // thereby disabling all DBC-checks. #define DBC_REQUIRE( ... ) #define DBC_ENSURE( ... ) #define DBC_INVARIANT( ... ) #define DBC_ASSERT( ... ) #endif #endif // DBC_H_