Creating Safe Arrays
As explained earlier in the chapter, you use Safe Array handles or Fortran Safe Array Structures to manipulate Safe Arrays in your Fortran applications and subroutines. The most elemental way to create a Safe Array in Fortran is to use f90VB’s SafeArrayCreate. The basic syntax for calling this subroutine is as follows [10]:
call SafeArrayCreate(SARef, vtType, nDims, IdxBounds)
SARef can be either a null Safe Array handle or an empty Fortran Safe Array Structure. Argument vtType is used to indicate the type of the elements in the Safe Array. f90VB defines several constants (globally referred to as VT-Types) that identify these types as shown in Table 2.1.
Argument nDims is the number of dimensions in the array. Argument IdxBounds is an array of SafeArrayBound that defines the bounds (i.e. lower bound and the number of elements) for each dimension of the array. SafeArrayBound has the following definition:
type SafeArrayBound
integer(4)::nElements
integer(4)::LowerBound
end type SafeArrayBound
To create a Safe Array of only one dimension, you can also use the following syntax to call SafeArrayCreate:
Call SafeArrayCreate(SARef, vtType, 1, LowerBound, nElements)
For 2-dimensional arrays, you can use:
call SafeArrayCreate(SARef, vtType, 2, LowerBound1, nElements1, &
LowerBound2, nElements2)
LowerBound1 and nElements1 are the bounds for the first dimension, and LowerBound2 and nElements2 are the bounds for the second dimension.
Safe Arrays created with SafeArrayCreate have some limitations. They are always dynamic arrays (i.e. they can be redimensioned) and you cannot create arrays of User Defined Types (UDT). Changing a dynamic array into a static array is easy, all you need to do is call SafeArraySetFeatures and change the array attributes to static (see the section entitled Setting and Reading Safe Array Features).
To work around the limitation regarding UDTs, you use a three-step approach to create the Safe Arrays; first you request an empty Array Descriptor from the OS (calling SafeArrayAllocDescriptor); second, you fill out the descriptor, indicating the characteristics of the Safe Array (including the size in bytes of your UDT); finally you request that the OS allocate the data-block for the Safe Array (calling SafeArrayAllocData).
Example 2.4 shows a short program that creates a Safe Array using the traditional method (i.e. calling SafeArrayCreate) and the three-step method. The program first creates a Safe Array (SAHndlA) using SafeArrayCreate and then initializes the array data using SafeArrayPutElement. The second section of the program creates a similar Safe Array, but this time uses the three-step method. This second Safe Array is accessed and initialized using SafeArrayAccessData, which is explained later in this chapter.
Example 2.4
program Example24
!Demonstrates several methods to create
!and access Safe Arrays with f90VB
use f90VBDefs
use f90VBSafeArrays
implicit none
integer(SAFEARRAY_KIND)::SAHndlA, SAHndlB
integer(LONG_KIND)::i,j,LBound, UBound
integer(HRESULT_KIND)::iRet
integer(SHORT_KIND)::vtType
real(REAL_KIND)::rValue
real(REAL_KIND),pointer::R4MappedMatrix(:,:)
!This section of code illustrates the use of
!SafeArrayCreate to create a Safe Array matrix
!of single precision reals
!Allocate the Safe Array (3x4)
Call SafeArrayCreate(SAHndlA,VT_R4, 1, 3, 1, 4)
!Print the information in the new descriptor
print *,'SAHndlA Descriptor:'
print *,'Handle:',SAHndlA
print *,'Dimensions:',SafeArrayGetDim(SAHndlA)
print *,'Element size:',SafeArrayGetElementSize(SAHndlA)
call SafeArrayGetVarType(SAHndlA,vtType,iRet)
print *,'Data Type:', vtType
print *,'Dimension Bounds:'
call SafeArrayGetLBound(SAHndlA,1,LBound,iRet)
call SafeArrayGetUBound(SAHndlA,1,UBound,iRet)
print *,'Dimension 1:',LBound,' to ', UBound
call SafeArrayGetLBound(SAHndlA,2,LBound,iRet)
call SafeArrayGetUBound(SAHndlA,2,UBound,iRet)
print *,'Dimension 2:',LBound,' to ', UBound
!Initialize all elements
call random_seed
do i=1,3
do j=1,4
rValue=i*10+j
call SafeArrayPutElement(SAHndlA,rValue,i,j,iRet)
enddo
enddo
!print array values
do i=1,3
do j=1,4
call SafeArrayGetElement(SAHndlA,rValue,i,j,iRet)
write(*,'(a1,i1,a1,i1,a2,f10.7)') &
'(',i,',',j,')=',rValue
enddo
enddo
!This section of code illustrates the
!three-step method to create a Safe Array
!matrix of single precision reals
!Allocate the array descriptor
call SafeArrayAllocDescriptor(SAHndlB, VT_R4, 2, iRet)
!Set the element size
call SafeArraySetElementSize(SAHndlB,4,iRet)
!Set the indexes for the dimensions
call SafeArraySetBounds(SAHndlB,1,1,3,iRet)
call SafeArraySetBounds(SAHndlB,2,1,4,iRet)
!Allocate the data block of the Safe Array
call SafeArrayAllocData(SAHndlB,iRet)
!Print the information in the new descriptor
print *,''
print *,'SAHndlB Descriptor:'
print *,'Handle:',SAHndlB
print *,'Dimensions:',SafeArrayGetDim(SAHndlB)
print *,'Element size:',SafeArrayGetElementSize(SAHndlB)
call SafeArrayGetVarType(SAHndlB,vtType,iRet)
print *,'Data Type:', vtType
print *,'Dimension Bounds:'
call SafeArrayGetLBound(SAHndlB,1,LBound,iRet)
call SafeArrayGetUBound(SAHndlB,1,UBound,iRet)
print *,'Dimension 1:',LBound,' to ', UBound
call SafeArrayGetLBound(SAHndlB,2,LBound,iRet)
call SafeArrayGetUBound(SAHndlB,2,UBound,iRet)
print *,'Dimension 2:',LBound,' to ', UBound
!Map R4MappedMatrix to the Safe Array
call SafeArrayAccessData(SAHndlB,R4MappedMatrix,iRet)
!Initialize elements in the Safe Array
!using mapped matrix
do i=1,3
do j=1,4
R4MappedMatrix(i,j)=i*10+j
enddo
enddo
!Print Safe Array using SafeArrayGetElement
do i=1,3
do j=1,4
call SafeArrayGetElement(SAHndlB,rValue,i,j,iRet)
write(*,'(a1,i1,a1,i1,a2,f10.7)') &
'(',i,',',j,')=',rValue
enddo
enddo
!But because R4MappedMatrix and SAHndlB share
!the same data, it would be easier and faster
!to print the Safe Array as:
print *,''
print *,'SAHndlB accessed through mapped array'
do i=1,3
write(*,'(4(f10.7,1x))') (R4MappedMatrix(i,j),j=1,4)
enddo
!Break the mapping and unlock the safe array
call SafeArrayUnAccessData(SAHndlB,iRet)
!Release memory used by mapped matrix
deallocate(R4MappedMatrix)
!Release memory used by the Safe Array
call SafeArrayDestroy(SAHndlA, iRet)
call SafeArrayDestroy(SAHndlB, iRet)
stop
end