7.3 Verify m4 Macros

Power is not revealed by striking hard or often, but by striking true. - Honore de Balzac (1799-1850)

The ``verify'' set of m4 macros enable the conditional compilation of the verification statements which are used to implement ``Design by Contract'' methodology in the CÆSAR Code Package. Verification statements7.1 are simply logical tests on variables which can be conditionally compiled into the code, allowing for extreme error checking if they are compiled in and unfettered execution speed if they are compiled out. The ``Design by Contract'' methodology extends this idea slightly by specifying when these verifications are to be done: input requirements are verified upon entry into a routine and output guarantees are verified before routine exit.

The CÆSAR Code Package further extends this idea in two ways:

The syntax for the VERIFY/WARN_IF macros is given by:

   VERIFY(<logical expression>,<activation level>)
   WARN_IF(<logical expression>,<activation level>)

where <logical expression> is the F90 test to be satisfied and <activation level> is the value of the appropriate LEVEL variable (DEBUG_LEVEL for VERIFY and WARNING_LEVEL for WARN_IF) at which the test is compiled into the executable program - for level values less than the <activation level>, the test is commented out. To reiterate, if the debug or warning level is not set high enough to activate a particular command, that command will be commented out and will not be executed by the code.

The level at which a particular command will activate should be set according to a combination of criteria: importance and cost. An expensive (in terms of cpu-time) command, ceteris paribus, should have a higher <activation level> than a cheaper command. Commands whose satisfaction is more important should have a lower <activation level>. Verification or warning commands that specify an <activation level> of -1 will always be executed.

Code compiled with lower debug or warning levels will necessarily be as fast as or faster than code compiled with higher levels, but will also include fewer checks for accuracy. A debug or warning level of zero should include only those checks that the code author thought were absolutely necessary. Setting the debug or warning level to -1 will eliminate all commands of the specified type (except those with activation levels of -1).

When a VERIFY/WARN_IF test is compiled into the code, an if-block is inserted which evaluates the test. If the test fails, an output message is printed which contains the text, filename, and line number of the failed test. VERIFY commands then abort program execution, while WARN_IF commands continue.

There is a slight difference in the coding for the Intrinsic Classes, because they are lower level than the communication routines, and therefore require serial coding. To trigger this difference, the Intrinsic Classes specify ``define([VERIFY_COMMUNICATION],[Local])'' in their headers.

As an example, assume that a file named verify_example contains these lines:

   m4_include(settings.m4)
   m4_include(verify.m4)

   VERIFY( i < 1, 5)
   VERIFY( j > 2, 6)
   VERIFY( k < 4, 7)

   define([DEBUG_LEVEL], 2)
   VERIFY( Valid_State(matrix), 1)
   VERIFY( Valid_State(matrix), 2)
   VERIFY( Valid_State(matrix), 3)

When this file is processed by Gnu m4, it is expanded into the following valid F90 code, if the DEBUG_LEVEL variable is not set:

   ! if (.not. Global_ALL(i < 1)) then
   !   if (this_is_IO_PE) then
   !     write (6,*) "Verification failed: ", &
   !                 "i < 1, ", &
   !                 "file verify_example, ", &
   !                 "line 4."
   !   end if
   !   call Abort
   ! end if
   ! if (.not. Global_ALL(j > 2)) then
   !   if (this_is_IO_PE) then
   !     write (6,*) "Verification failed: ", &
   !                 "j > 2, ", &
   !                 "file verify_example, ", &
   !                 "line 5."
   !   end if
   !   call Abort
   ! end if
   ! if (.not. Global_ALL(k < 4)) then
   !   if (this_is_IO_PE) then
   !     write (6,*) "Verification failed: ", &
   !                 "k < 4, ", &
   !                 "file verify_example, ", &
   !                 "line 6."
   !   end if
   !   call Abort
   ! end if
 
    if (.not. Global_ALL(Valid_State(matrix))) then
      if (this_is_IO_PE) then
        write (6,*) "Verification failed: ", &
                    "Valid_State(matrix), ", &
                    "file verify_example, ", &
                    "line 9."
      end if
      call Abort
    end if
    if (.not. Global_ALL(Valid_State(matrix))) then
      if (this_is_IO_PE) then
        write (6,*) "Verification failed: ", &
                    "Valid_State(matrix), ", &
                    "file verify_example, ", &
                    "line 10."
      end if
      call Abort
    end if
   ! if (.not. Global_ALL(Valid_State(matrix))) then
   !   if (this_is_IO_PE) then
   !     write (6,*) "Verification failed: ", &
   !                 "Valid_State(matrix), ", &
   !                 "file verify_example, ", &
   !                 "line 11."
   !   end if
   !   call Abort
   ! end if

If the DEBUG_LEVEL m4 variable is set to a value of 6, via the command line option -DDEBUG_LEVEL=6, then the expansion becomes:

    if (.not. Global_ALL(i < 1)) then
      if (this_is_IO_PE) then
        write (6,*) "Verification failed: ", &
                    "i < 1, ", &
                    "file verify_example, ", &
                    "line 4."
      end if
      call Abort
    end if
    if (.not. Global_ALL(j > 2)) then
      if (this_is_IO_PE) then
        write (6,*) "Verification failed: ", &
                    "j > 2, ", &
                    "file verify_example, ", &
                    "line 5."
      end if
      call Abort
    end if
   ! if (.not. Global_ALL(k < 4)) then
   !   if (this_is_IO_PE) then
   !     write (6,*) "Verification failed: ", &
   !                 "k < 4, ", &
   !                 "file verify_example, ", &
   !                 "line 6."
   !   end if
   !   call Abort
   ! end if

    if (.not. Global_ALL(Valid_State(matrix))) then
      if (this_is_IO_PE) then
        write (6,*) "Verification failed: ", &
                    "Valid_State(matrix), ", &
                    "file verify_example, ", &
                    "line 9."
      end if
      call Abort
    end if
    if (.not. Global_ALL(Valid_State(matrix))) then
      if (this_is_IO_PE) then
        write (6,*) "Verification failed: ", &
                    "Valid_State(matrix), ", &
                    "file verify_example, ", &
                    "line 10."
      end if
      call Abort
    end if
   ! if (.not. Global_ALL(Valid_State(matrix))) then
   !   if (this_is_IO_PE) then
   !     write (6,*) "Verification failed: ", &
   !                 "Valid_State(matrix), ", &
   !                 "file verify_example, ", &
   !                 "line 11."
   !   end if
   !   call Abort
   ! end if

If the WARN_IF macro had been used instead of the VERIFY macro (and the WARNING_LEVEL were set instead of the DEBUG_LEVEL), then the only changes in the output of the above examples would be that the Abort call would not have appeared, the test would not have been negated, and a Global_ANY would have been used instead of a Global_ALL.

If the VERIFY_COMMUNICATION macro had been set to Local, then the Global_ALL call would not have been used, the IO_PE check would not have appeared, and the Abort call would have been replaced with a stop.

As an additional refinement, any quotation characters in the logical expression are removed from the output string to prevent confusion with quoting there. Quotation marks in the logical expression should therefore be paired.

Note that this set of m4 macros depends on the m4 commands in the settings.m4 file and on the DEBUG_LEVEL and WARNING_LEVEL macro definitions.

m4 macros defined in the include/verify.m4 file:

 DEBUG_LEVEL  Verification is turned on for VERIFY commands whose activation level is equal to or less than the DEBUG_LEVEL.
 VERIFY  Compile in or comment out a test which halts execution if unsatisfied.
 WARNING_LEVEL  Warnings are turned on for WARN_IF commands whose activation level is equal to or less than the WARNING_LEVEL.
 WARN_IF  Compile in or comment out a test which prints a warning and continues execution if satisfied.

The Verify m4 Macros code listing contains additional documentation.

Michael L. Hall