Visual Basic (OLE/COM) Safe Arrays and Fortran Dynamic Arrays
If you checked OLE’s header files (they are written in C), you would find that OLE arrays are much more than just a block of memory containing data organized in a tabular fashion. Indeed, a Safe Array contains two sections; an Array Descriptor, that contains information about the dimensions and type of data stored in the array, and a Data Block, that contains the array data. The Array Descriptor is a structure that is represented in C as follows:
struct SAFEARRAY {
WORD cDims;
WORD fFeatures;
DWORD cbElements;
DWORD cLocks;
void * pvData;
SAFEARRAYBOUND rgsabound[1];
};
An equivalent Fortran definition for this structure is:
type SafeArray
integer(2)::cDims
integer(2)::fFeatures
integer(4)::cbElements
integer(2)::Locks
pointer::pvData
type(SAFEARRAYBOUND):: rgsabound(:)
end type SafeArray
The previous declaration, of course, will not compile in most Fortran compilers. The compiler will likely throw an error when it finds the declaration of field pvData, because Fortran doesn’t implement pointers the same way C does. If you are using f90VB’s Safe Array library, you’ll never have to compile the Fortran declaration above. Still this pseudo-Fortran declaration may help you understand what a Safe Array descriptor is and how it works.
The first field (cDims) of the Array Descriptor contains the number of dimensions of the array. The fFeatures field is a bit field indicating attributes of a particular array (e.g. type of data contained in the safe array). The cbElements field defines the size of each element in the array. As in Fortran all elements of a Safe Array have the same size [05]. The cLocks field is a reference count that indicates how many times the array has been locked. When there is no lock, you're not supposed to access the array data, which is located starting at the address pointed by pvData.
The last field in an Array Descriptor (rgsabound) is an array of boundary structures. This array contains an entry for each dimension in the Safe Array. By default, rgsabound has only one element (i.e. enough to describe a single dimension), but if you define multiple dimensions, the appropriate f90VB function will reallocate the rgsabound to give you as many array elements as you need. The dimension array is the last member of the array so that it can expand. A SAFEARRAYBOUND structure looks like this in Fortran:
type SafeArrayBound
integer(4)::nElements
integer(4)::LowerBound
end type SafeArrayBound
The nElements field has the number of elements in the dimension, and the LowerBound field has the lower boundary of the dimension.
If you take the time to think a little about the possibilities of this method to define arrays, you would realize that it offers an incredible amount of flexibility, and perhaps more important, it offers type and dimension safety. Because a Safe Array is self-describing, a subroutine that receives a Safe Array can always know the type of data contained and the dimensions of the array. Now, before you go kiss Bill Gates for having such a brilliant idea, let’s take a look at the way Fortran handles dynamic arrays.
Starting with Fortran 90, Fortran programmers can declare dynamic arrays of two types, allocatable and pointer arrays. An allocatable array can be declared as:
real(4),allocatable:: MyArray(:)
A similar pointer array can be declared as:
real(4),pointer::MyArray(:)
In both cases, the number of elements in the array can be set at run-time, by using Fortran’s intrinsic function Allocate.
Behind the curtains Fortran compilers maintain dynamic arrays through the use of array descriptors. Unfortunately, the Fortran 90/95 standards leave the details of implementing descriptors to compiler developers, so each compiler has its particular implementation of array descriptors. Compaq (Digital) Visual Fortran documents their implementation of array descriptors in their manuals. It looks much like this:
type Descriptor
integer(4) Base ! Base address
integer(4) Len ! Length of data type
integer(4) Offset ! Offset from Base address
integer(4) Reserved ! Reserved for future use
integer(4) Rank ! Rank of pointer
type(DescriptorTriplet) Dim(DescriptorMaxRank)
end type
type DescriptorTriplet
integer(4) Extent ! Number of Elements in this dimension
integer(4) Mult ! Multiplier for this dimension
integer(4) LowerBound ! LowerBound of this dimension
end type
Although we won’t get into the details of this Fortran descriptor, if you read the beginning of this section, this should look familiar enough that you can figure out most of it by yourself. The sum of fields Base and Offset give you a pointer to the location of the array data. Other Fortran 90/95 compilers implement array descriptors in different ways, but they are all variations around the same principle; a structure that contains general information about the array, including a pointer to the location of the data, and a set of descriptions for each of the dimensions of the array. When you call Fortran subroutines with arguments declared as allocatable or pointer array, the Fortran compiler does not pass an array, but rather a pointer to the descriptor that characterizes the array. So after all, Fortran must not be as ancient a language as some people insist it is.
At this point you may be wondering why you’ve never had to use array descriptors in your Fortran or Visual Basic programs. The reason is simple, you are not supposed to. Array descriptors must be handled very carefully through a set of strict rules. This is usually done better by compilers than by us humans who are so fond of breaking the rules (either intentionally or by mistake). Fortran and Visual Basic array descriptors are not even directly available to programmers in these languages. Safe Arrays, however, are also part of the OLE/COM specification, which is supposed to be a language-independent standard. For this reason Microsoft provides a set of APIs that can be used to manipulate Safe Arrays efficiently. F90VB’s Safe Arrays library is built around these APIs.
As with other OLE/COM data types, Safe Arrays are allocated by the operating system and referenced through handles. Your Fortran program creates a Safe Array by sending a request to the operating system (for example using f90VB’s subroutine SafeArrayCreate). The operating system returns a handle to the array descriptor, which your program then uses to manipulate its data. Since you manipulate the Safe Array through a set of API procedures, you must be aware of the rules that govern Safe Array manipulation to avoid potential memory leaks or protection faults. The next section explains these rules in detail.
Seven basic rules for manipulating Safe Arrays