I am looking at some code and there is this:
BLOCK
...lines of code...
END BLOCK
What is the purpose of BLOCK
? I tried to google it, but all I found was something about BLOCK DATA
and COMMON
blocks. I'm not sure whether they are related.
From the Fortran 2008 standard:
The BLOCK construct is an executable construct that may contain declarations.
It is not related to common blocks or to block data program units.
So, the main use is this "containing declarations".
As scoping units we have things like
integer i
block
integer j ! A local integer
integer i ! Another i
save i ! ... which can even be SAVEd
end block
which affords locality of declarations:
! ... lots of code
block
integer something
read *, something
end block
! ... lots more code
These scoping blocks allow automatic objects:
integer i
i = 5
block
real x(i)
end block
As executable constructs, they also have useful flow control:
this_block: block
if (something) exit this_block
! ... lots of code
end block
They also have finalization control:
type(t) x
block
type(t) y
end block ! y is finalized
end ! x is not finalized
for x
and y
of finalizable type.
Oh, and let's not forget how you can confuse people with implicit typing.
I have composed a few more fun examples. What if you want to invoke an assumed length function
with different lengths
in the same instance of a subprogram? You need a specification statement
to tell the compiler the length you want, so a BLOCK
construct can do this for you
function F(x)
implicit none
character(*) F
character x(:)
integer i
do i = 1, len(F)
F(i:i) = x(1)
end do
end function F
program blox1
implicit none
integer i
character c
do i = 1, 7
c = achar(65+modulo(i**4+6*i**2+1,26))
block
character(2*i+1), external :: F
call blox1a(F)
end block
end do
contains
subroutine blox1a(F)
interface
function F(x)
import
implicit none
character(2*i+1) F
character x(:)
end function F
end interface
write(*,'(a)') F([c])
end subroutine blox1a
end program blox1
Output with gfortran
:
III
PPPPP
GGGGGGG
PPPPPPPPP
WWWWWWWWWWW
FFFFFFFFFFFFF
SSSSSSSSSSSSSSS
Or how about when you need the appropriate KIND
for a REAL literal
? This requires a named constant
and the KIND
might be given in the specification statements
of another MODULE
and may even be given as an expression
. In that case you might try defining a named constant
with the value of that expression
, but if an unlucky choice is made that name might override another host associated
name. A BLOCK
construct makes it all OK:
module mytypes
use ISO_FORTRAN_ENV
implicit none
type T(KIND)
integer, kind :: KIND
real(KIND) x
end type T
interface assignment(=)
module procedure assign8, assign4
end interface assignment(=)
contains
subroutine assign8(x,y)
real(REAL64), intent(in) :: y
type(T(kind(y))), intent(out) :: x
x%x = y
end subroutine assign8
subroutine assign4(x,y)
real(REAL32), intent(in) :: y
type(T(kind(y))), intent(out) :: x
x%x = y
end subroutine assign4
end module mytypes
program blox2
use mytypes
implicit none
type(T(REAL32)) x
BLOCK
! integer, parameter :: rk = x%KIND ! Not allowed
integer, parameter :: rk = kind(x%x)
x = 0.0072973525664_rk
write(*,'(g0)') x%x
END BLOCK -1 is too small
12! = 479001600
13 is too big
BLOCK
type(T(REAL64)) x
BLOCK
! integer, parameter :: rk = x%KIND ! Not allowed
integer, parameter :: rk = kind(x%x)
x = 0.0072973525664_rk
write(*,'(g0)') x%x
END BLOCK
END BLOCK
end program blox2
Output with gfortran
:
0.729735242E-02
0.72973525663999998E-002
It can be tricky to get a Fortran pointer
to a C string
because there is no syntax to tell C_F_POINTER
what the length
of the target
of a deferred length pointer
should be. BLOCK
to the rescue!
program blox3
use ISO_C_BINDING
implicit none
character(len=:,kind=C_CHAR), allocatable, target :: x
type(C_PTR) c_hello
integer(C_INTPTR_T) address
character(kind=C_CHAR), pointer :: nul_address
character(len=:,kind=C_CHAR), pointer :: f_hello
integer i
x = 'Hello, world'//achar(0)
c_hello = C_LOC(x(1:1))
address = transfer(c_hello,address)
i = 0
do
call C_F_POINTER(transfer(address+i,C_NULL_PTR),nul_address)
if(nul_address == C_NULL_CHAR) exit
i = i+1
end do
BLOCK
character(len=i,kind=C_CHAR), pointer :: temp
call C_F_POINTER(c_hello,temp)
f_hello => temp
END BLOCK
write(*,'(i0,1x,a)') len(f_hello), f_hello
end program blox3
Output with gfortran
:
12 Hello, world
Not to mention that a named BLOCK construct
gives us a label
to hang our spaghetti code
on:
program blox4
implicit none
integer i
integer j(3)
integer k
j = [-1,12,13]
do i = 1, size(j)
factorial: BLOCK
if(j(i) < 0) then
write(*,'(*(g0))') j(i),' is too small'
EXIT factorial
end if
if(j(i) > 12) then
write(*,'(*(g0))') j(i),' is too big'
EXIT factorial
end if
write(*,'(*(g0))') j(i),'! = ',product([(k,k=1,j(i))])
END BLOCK factorial
end do
end program blox4
Output with gfortran
:
-1 is too small
12! = 479001600
13 is too big
The block
construct allows you to declare entities such as variables, types, external procedures, etc which are locally known to the block but have no effect to any variables outside of the block.
Example 1:
IF (swapxy) THEN
BLOCK
REAL (KIND (x)) tmp
tmp = x
x = y
y = tmp
END BLOCK
END IF
Here the variable tmp
is locally defined to help out with the swap of two variables. Outside of the block, the variable tmp
is unknown or back its original form if it was defined outside of the block (see next example).
Example 2:
F = 254E-2
BLOCK
REAL F
F = 39.37
END BLOCK
! F is still equal to 254E-2.
The variable F
is redefined locally to the block but has no effect outside of it.
These kinds of blocks are used to make the code more readable and easier to understand as you don't need to look to the entire subprogram to understand what the locally defined entities are. Furthermore, the compiler knows that these entities are locally defined, so it might do some more optimization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With