diff --git a/README.md b/README.md index cb0c5a8..ff6c6c4 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ preprocessor ASSERTIONS to non-zero, eg: ``` fpm build --flag "-DASSERTIONS" ``` -The program [example/invoke-via-macro.F90] demonstrates the preferred way to invoke the `assert` subroutine via the three provided macros. -Invoking `assert` this way insures that `assert` invocations will be completely removed whenever the `ASSERTIONS` macro is undefined (or defined to zero) during compilation. +The program [example/invoke-via-macro.F90] demonstrates the preferred way to invoke assertions via the three provided macros. +Invoking assertions this way ensures such calls will be completely removed whenever the `ASSERTIONS` macro is undefined (or defined to zero) during compilation. Due to a limitation of `fpm`, this approach works best if the project using Assert is also a `fpm` project. If instead `fpm install` is used, then either the user must copy `include/assert_macros.h` to the installation directory (default: `~/.local/include`) or the user must invoke `assert` directly (via `call assert(...)`). diff --git a/example/invoke-via-macro.F90 b/example/invoke-via-macro.F90 index 12212f1..d4dc895 100644 --- a/example/invoke-via-macro.F90 +++ b/example/invoke-via-macro.F90 @@ -3,9 +3,10 @@ program invoke_via_macro !! Demonstrate how to invoke the 'assert' subroutine using a preprocessor macro that facilitates !! the complete removal of the call in the absence of the compiler flag: -DASSERTIONS - use assert_m, only : assert, intrinsic_array_t, string - !! If an "only" clause is employed as above, it must include the "string" function that the - !! call_assert* macros reference when transforming the code below into "assert" subroutine calls. + use assert_m ! <--- this is the recommended use statement + !! If an "only" clause is employed above, the symbols required by the + !! macro expansion are subject to change without notice between versions. + !! You have been warned! implicit none #if !ASSERTIONS @@ -15,7 +16,7 @@ program invoke_via_macro print * #endif - ! The C preprocessor will convert each call_assert* macro below into calls to the "assert" subroutine + ! The C preprocessor will convert each call_assert* macro below into calls that enforce the assertion ! whenever the ASSERTIONS macro is defined to non-zero (e.g. via the -DASSERTIONS compiler flag). ! Whenever the ASSERTIONS macro is undefined or defined to zero (e.g. via the -DASSERTIONS=0 compiler flag), ! these calls will be entirely removed by the preprocessor. diff --git a/include/assert_macros.h b/include/assert_macros.h index f5cf4db..4a058a9 100644 --- a/include/assert_macros.h +++ b/include/assert_macros.h @@ -22,9 +22,9 @@ #endif #if ASSERTIONS -# define call_assert(assertion) call assert(assertion, "call_assert(" // STRINGIFY(assertion) // ") in file " // __FILE__ // ", line " // string(__LINE__)) -# define call_assert_describe(assertion, description) call assert(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__)) -# define call_assert_diagnose(assertion, description, diagnostic_data) call assert(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__), diagnostic_data) +# define call_assert(assertion) call assert_always(assertion, "call_assert(" // STRINGIFY(assertion) // ") in file " // __FILE__ // ", line " // string(__LINE__)) +# define call_assert_describe(assertion, description) call assert_always(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__)) +# define call_assert_diagnose(assertion, description, diagnostic_data) call assert_always(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__), diagnostic_data) #else # define call_assert(assertion) # define call_assert_describe(assertion, description) diff --git a/src/assert/assert_subroutine_m.F90 b/src/assert/assert_subroutine_m.F90 index 7ac135c..d66483c 100644 --- a/src/assert/assert_subroutine_m.F90 +++ b/src/assert/assert_subroutine_m.F90 @@ -33,7 +33,7 @@ module assert_subroutine_m !! implicit none private - public :: assert + public :: assert, assert_always #ifndef USE_ASSERTIONS # if ASSERTIONS @@ -47,7 +47,8 @@ module assert_subroutine_m interface pure module subroutine assert(assertion, description, diagnostic_data) - !! If assertion is .false., error-terminate with a character stop code that contains diagnostic_data if present + !! If assertion is .false. and enforcement is enabled (e.g. via -DASSERTIONS=1), + !! then error-terminate with a character stop code that contains diagnostic_data if present implicit none logical, intent(in) :: assertion !! Most assertions will be expressions such as i>0 @@ -57,6 +58,14 @@ pure module subroutine assert(assertion, description, diagnostic_data) !! Data to include in an error ouptput: may be of an intrinsic type or a type that extends characterizable_t end subroutine + pure module subroutine assert_always(assertion, description, diagnostic_data) + !! Same as above but always enforces the assertion (regardless of ASSERTIONS) + implicit none + logical, intent(in) :: assertion + character(len=*), intent(in) :: description + class(*), intent(in), optional :: diagnostic_data + end subroutine + end interface end module assert_subroutine_m diff --git a/src/assert/assert_subroutine_s.F90 b/src/assert/assert_subroutine_s.F90 index 157b962..58a0372 100644 --- a/src/assert/assert_subroutine_s.F90 +++ b/src/assert/assert_subroutine_s.F90 @@ -10,12 +10,18 @@ contains module procedure assert - use characterizable_m, only : characterizable_t - - character(len=:), allocatable :: header, trailer toggle_assertions: & if (enforce_assertions) then + call assert_always(assertion, description, diagnostic_data) + end if toggle_assertions + + end procedure + + module procedure assert_always + use characterizable_m, only : characterizable_t + + character(len=:), allocatable :: header, trailer check_assertion: & if (.not. assertion) then @@ -59,8 +65,6 @@ end if check_assertion - end if toggle_assertions - contains pure function string(numeric) result(number_as_string) diff --git a/test/test-assert-macro.F90 b/test/test-assert-macro.F90 index 62372ca..609377b 100644 --- a/test/test-assert-macro.F90 +++ b/test/test-assert-macro.F90 @@ -71,7 +71,32 @@ program test_assert_macros #undef ASSERTIONS #include "assert_macros.h" - call_assert_describe(.false., "") - print *," passes on being removed by the preprocessor when ASSERTIONS is undefined" + call_assert_diagnose(.false., "", "") + print *," passes on being removed by the preprocessor when ASSERTIONS is undefined" // new_line('') + + !------------------------------------------ + +#undef ASSERTIONS +#define ASSERTIONS 1 +#include "assert_macros.h" + print *,"The call_assert_* macros" + block + logical :: foo + foo = check_assert(.true.) + print *," pass on invocation from a pure function" + end block + +contains + + pure function check_assert(cond) result(ok) + logical, intent(in) :: cond + logical ok + + call_assert(cond) + call_assert_describe(cond, "check_assert") + call_assert_diagnose(cond, "check_assert", "") + + ok = .true. + end function end program