ensure.h

Back to Invariant checking

pastel/sys/

// Description: Invariant checking
// Detail: Contains ENSURE, PENSURE, and ASSERT macros.

#ifndef PASTELSYS_ENSURE_H
#define PASTELSYS_ENSURE_H

#include "pastel/sys/mytypes.h"

#if (defined _WIN32 || defined _WIN64)
#  define PASTEL_FUNCTION_NAME __FUNCTION__
#else
#  define PASTEL_FUNCTION_NAME __func__
#endif

// Reports

#define REPORT(expr)\
   ((expr) && (Pastel::Ensure_::report(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__), true))

#define REPORT1(expr, a)\
   ((expr) && (Pastel::Ensure_::report(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a)), true))

#define REPORT2(expr, a, b)\
   ((expr) && (Pastel::Ensure_::report(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b)), true))

#define REPORT3(expr, a, b, c)\
   ((expr) && (Pastel::Ensure_::report(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b), Pastel::Ensure_::parameterInfo(#c, c)), true))

#define REPORT4(expr, a, b, c, d)\
   ((expr) && (Pastel::Ensure_::report(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b), Pastel::Ensure_::parameterInfo(#c, c), Pastel::Ensure_::parameterInfo(#d, d)), true))

#define REPORT_OP(x, op, y) REPORT2(x op y, x, y)

// Errors

#define ENSURE(expr)\
{if (!(expr)) {Pastel::Ensure_::error(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__);}}

#define ENSURE1(expr, a)\
{if (!(expr)) {Pastel::Ensure_::error(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a));}}

#define ENSURE2(expr, a, b)\
{if (!(expr)) {Pastel::Ensure_::error(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b));}}

#define ENSURE3(expr, a, b, c)\
{if (!(expr)) {Pastel::Ensure_::error(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b), Pastel::Ensure_::parameterInfo(#c, c));}}

#define ENSURE4(expr, a, b, c, d)\
{if (!(expr)) {Pastel::Ensure_::error(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b), Pastel::Ensure_::parameterInfo(#c, c), Pastel::Ensure_::parameterInfo(#d, d));}}

#define ENSURE_OP(x, op, y) ENSURE2(x op y, x, y)
#define ENSURE_RANGE(x, min, max) ENSURE3(min <= x && x < max, x, min, max)

// PENSURES

#if (defined(DEBUG) || defined(PASTEL_ENABLE_ASSERTS))

#define PENSURE(expr) ENSURE(expr)
#define PENSURE1(expr, a) ENSURE1(expr, a)
#define PENSURE2(expr, a, b) ENSURE2(expr, a, b)
#define PENSURE3(expr, a, b, c) ENSURE3(expr, a, b, c)
#define PENSURE4(expr, a, b, c, d) ENSURE4(expr, a, b, c, d)
#define PENSURE_OP(x, op, y) ENSURE_OP(x, op, y)
#define PENSURE_RANGE(x, min, max) ENSURE_RANGE(x, min, max)

#else

#define PENSURE(expr) 
#define PENSURE1(expr, a) 
#define PENSURE2(expr, a, b) 
#define PENSURE3(expr, a, b, c) 
#define PENSURE4(expr, a, b, c, d) 
#define PENSURE_OP(x, op, y) 
#define PENSURE_RANGE(x, min, max)

#endif

// Assertions

#if (defined(DEBUG) || defined(PASTEL_ENABLE_ASSERTS))

#define ASSERT(expr)\
{if (!(expr)) {Pastel::Ensure_::assertionError(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__);}}

#define ASSERT1(expr, a)\
{if (!(expr)) {Pastel::Ensure_::assertionError(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a));}}

#define ASSERT2(expr, a, b)\
{if (!(expr)) {Pastel::Ensure_::assertionError(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b));}}

#define ASSERT3(expr, a, b, c)\
{if (!(expr)) {Pastel::Ensure_::assertionError(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b), Pastel::Ensure_::parameterInfo(#c, c));}}

#define ASSERT4(expr, a, b, c, d)\
{if (!(expr)) {Pastel::Ensure_::assertionError(#expr, PASTEL_FUNCTION_NAME, __FILE__, __LINE__, Pastel::Ensure_::parameterInfo(#a, a), Pastel::Ensure_::parameterInfo(#b, b), Pastel::Ensure_::parameterInfo(#c, c), Pastel::Ensure_::parameterInfo(#d, d));}}

#define ASSERT_OP(x, op, y) ASSERT2(x op y, x, y)
#define ASSERT_RANGE(x, min, max) ASSERT3(min <= x && x < max, x, min, max)

#else

#define ASSERT(expr)
#define ASSERT1(expr, a)
#define ASSERT2(expr, a, b)
#define ASSERT3(expr, a, b, c)
#define ASSERT4(expr, a, b, c, d)
#define ASSERT_OP(x, op, y)
#define ASSERT_RANGE(x, min, max)

#endif

namespace Pastel
{

    class InvariantFailure {};

    namespace Ensure_
    {

        template <typename Type>
        struct ParameterInfo
        {
            const char* name;
            Type value;
        };

        template <typename Type>
        ParameterInfo<Type> parameterInfo(
            const char* name, Type&& value)
        {
            return {name, std::forward<Type>(value)};
        }

        //! Prints a report message. Used by REPORT macros.
        template <typename... TypeSet>
        void report(
            const char* testText,
            const char* functionName,
            const char* fileName, 
            int lineNumber,
            ParameterInfo<TypeSet>... parameterSet);

        //! Prints an error message and aborts the program.
        template <typename... TypeSet>
        void error(
            const char* testText,
            const char* functionName,
            const char* fileName, 
            int lineNumber,
            ParameterInfo<TypeSet>... parameterSet);

        //! Prints an error message and aborts the program.
        template <typename... TypeSet>
        void assertionError(
            const char* testText,
            const char* functionName,
            const char* fileName, 
            int lineNumber,
            ParameterInfo<TypeSet>... parameterSet);

    }

}

#include "pastel/sys/ensure.hpp"

#endif