Skip to content

Commit 8e33ee8

Browse files
authored
Merge pull request #43 from bonachea/assert_always
Assert always
2 parents 2bd9f8f + b37b5d9 commit 8e33ee8

6 files changed

Lines changed: 57 additions & 18 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ preprocessor ASSERTIONS to non-zero, eg:
3535
```
3636
fpm build --flag "-DASSERTIONS"
3737
```
38-
The program [example/invoke-via-macro.F90] demonstrates the preferred way to invoke the `assert` subroutine via the three provided macros.
39-
Invoking `assert` this way insures that `assert` invocations will be completely removed whenever the `ASSERTIONS` macro is undefined (or defined to zero) during compilation.
38+
The program [example/invoke-via-macro.F90] demonstrates the preferred way to invoke assertions via the three provided macros.
39+
Invoking assertions this way ensures such calls will be completely removed whenever the `ASSERTIONS` macro is undefined (or defined to zero) during compilation.
4040
Due to a limitation of `fpm`, this approach works best if the project using Assert is also a `fpm` project.
4141
If instead `fpm install` is used, then either the user must copy `include/assert_macros.h` to the installation directory (default: `~/.local/include`) or
4242
the user must invoke `assert` directly (via `call assert(...)`).

example/invoke-via-macro.F90

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
program invoke_via_macro
44
!! Demonstrate how to invoke the 'assert' subroutine using a preprocessor macro that facilitates
55
!! the complete removal of the call in the absence of the compiler flag: -DASSERTIONS
6-
use assert_m, only : assert, intrinsic_array_t, string
7-
!! If an "only" clause is employed as above, it must include the "string" function that the
8-
!! call_assert* macros reference when transforming the code below into "assert" subroutine calls.
6+
use assert_m ! <--- this is the recommended use statement
7+
!! If an "only" clause is employed above, the symbols required by the
8+
!! macro expansion are subject to change without notice between versions.
9+
!! You have been warned!
910
implicit none
1011

1112
#if !ASSERTIONS
@@ -15,7 +16,7 @@ program invoke_via_macro
1516
print *
1617
#endif
1718

18-
! The C preprocessor will convert each call_assert* macro below into calls to the "assert" subroutine
19+
! The C preprocessor will convert each call_assert* macro below into calls that enforce the assertion
1920
! whenever the ASSERTIONS macro is defined to non-zero (e.g. via the -DASSERTIONS compiler flag).
2021
! Whenever the ASSERTIONS macro is undefined or defined to zero (e.g. via the -DASSERTIONS=0 compiler flag),
2122
! these calls will be entirely removed by the preprocessor.

include/assert_macros.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
#endif
2323

2424
#if ASSERTIONS
25-
# define call_assert(assertion) call assert(assertion, "call_assert(" // STRINGIFY(assertion) // ") in file " // __FILE__ // ", line " // string(__LINE__))
26-
# define call_assert_describe(assertion, description) call assert(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__))
27-
# define call_assert_diagnose(assertion, description, diagnostic_data) call assert(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__), diagnostic_data)
25+
# define call_assert(assertion) call assert_always(assertion, "call_assert(" // STRINGIFY(assertion) // ") in file " // __FILE__ // ", line " // string(__LINE__))
26+
# define call_assert_describe(assertion, description) call assert_always(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__))
27+
# define call_assert_diagnose(assertion, description, diagnostic_data) call assert_always(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__), diagnostic_data)
2828
#else
2929
# define call_assert(assertion)
3030
# define call_assert_describe(assertion, description)

src/assert/assert_subroutine_m.F90

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ module assert_subroutine_m
3333
!!
3434
implicit none
3535
private
36-
public :: assert
36+
public :: assert, assert_always
3737

3838
#ifndef USE_ASSERTIONS
3939
# if ASSERTIONS
@@ -47,7 +47,8 @@ module assert_subroutine_m
4747
interface
4848

4949
pure module subroutine assert(assertion, description, diagnostic_data)
50-
!! If assertion is .false., error-terminate with a character stop code that contains diagnostic_data if present
50+
!! If assertion is .false. and enforcement is enabled (e.g. via -DASSERTIONS=1),
51+
!! then error-terminate with a character stop code that contains diagnostic_data if present
5152
implicit none
5253
logical, intent(in) :: assertion
5354
!! Most assertions will be expressions such as i>0
@@ -57,6 +58,14 @@ pure module subroutine assert(assertion, description, diagnostic_data)
5758
!! Data to include in an error ouptput: may be of an intrinsic type or a type that extends characterizable_t
5859
end subroutine
5960

61+
pure module subroutine assert_always(assertion, description, diagnostic_data)
62+
!! Same as above but always enforces the assertion (regardless of ASSERTIONS)
63+
implicit none
64+
logical, intent(in) :: assertion
65+
character(len=*), intent(in) :: description
66+
class(*), intent(in), optional :: diagnostic_data
67+
end subroutine
68+
6069
end interface
6170

6271
end module assert_subroutine_m

src/assert/assert_subroutine_s.F90

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@
1010
contains
1111

1212
module procedure assert
13-
use characterizable_m, only : characterizable_t
14-
15-
character(len=:), allocatable :: header, trailer
1613

1714
toggle_assertions: &
1815
if (enforce_assertions) then
16+
call assert_always(assertion, description, diagnostic_data)
17+
end if toggle_assertions
18+
19+
end procedure
20+
21+
module procedure assert_always
22+
use characterizable_m, only : characterizable_t
23+
24+
character(len=:), allocatable :: header, trailer
1925

2026
check_assertion: &
2127
if (.not. assertion) then
@@ -59,8 +65,6 @@
5965

6066
end if check_assertion
6167

62-
end if toggle_assertions
63-
6468
contains
6569

6670
pure function string(numeric) result(number_as_string)

test/test-assert-macro.F90

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,32 @@ program test_assert_macros
7171

7272
#undef ASSERTIONS
7373
#include "assert_macros.h"
74-
call_assert_describe(.false., "")
75-
print *," passes on being removed by the preprocessor when ASSERTIONS is undefined"
74+
call_assert_diagnose(.false., "", "")
75+
print *," passes on being removed by the preprocessor when ASSERTIONS is undefined" // new_line('')
76+
77+
!------------------------------------------
78+
79+
#undef ASSERTIONS
80+
#define ASSERTIONS 1
81+
#include "assert_macros.h"
82+
print *,"The call_assert_* macros"
83+
block
84+
logical :: foo
85+
foo = check_assert(.true.)
86+
print *," pass on invocation from a pure function"
87+
end block
88+
89+
contains
90+
91+
pure function check_assert(cond) result(ok)
92+
logical, intent(in) :: cond
93+
logical ok
94+
95+
call_assert(cond)
96+
call_assert_describe(cond, "check_assert")
97+
call_assert_diagnose(cond, "check_assert", "")
98+
99+
ok = .true.
100+
end function
76101

77102
end program

0 commit comments

Comments
 (0)