contents   index   previous   next



Checking the type and obtaining the contents of a Variant

 

With the knowledge you already have about Variants, you can probably infer that checking the type of Variant and obtaining its contents is a very simple operation; all you need to do is retrieve and test the value in the field vt, then you can use the appropriate field (iVal, dblVal, etc.) to retrieve the content, right?. Well, maybe. There is one thing you should be aware of; the value in the vt field may be masked with VT_ARRAY, VT_VECTOR or VT_BYREF to indicate that the Variant contains a reference to a Safe Array, a C-style Vector or a pointer to a variable of the base type. When any of these modifiers (or masks) are present, the Variant does not contain the value, but a pointer to the value [18]. This situation creates two problems for Fortran programmers, which are better explained with an example. Let’s say you have written a Fortran subroutine that accepts a Variant argument:

 

subroutine MySub(varArg)
   type(VARIANT),intent(in)::VarArg
   real(DOUBLE_KIND)::MayBeWrongDouble
   real(DOUBLE_KIND)::MayNeverBeAssignedDouble
   real(DOUBLE_KIND)::FinallyAGoodDouble
   type(VARIANT)::varTmp 
   …
end subroutine MySyb

 

Somewhere inside the subroutine you need to check if the type of the Variant passed in varArg is a double-precision real, so you go ahead and perform the following conditional and assignment statements:

 

if (varArg%varVal%vt.eq.VT_R8) then
   MayNeverBeAssignedDouble = varArg%varVal%dblVal
   …
endif

 

Variable MayNeverBeAssignedDouble may never be assigned, even if varArg contains a valid reference to a double-precision value. Why?, because the vt field may be masked with a VT_BYREF to indicate that what varArg contains is a pointer to a double precision value, rather than the value itself. In other words, the vt field could contain the result of

 

ior(VT_I8,VT_BYREF)

 

Which is still a valid reference to a double-precision value from a Variant point of view. So we know that a simple test of equality to VT_R8 doesn’t work. What then? As some of you may have guessed, you can also test the vt field of a Variant using Fortran’s bit-wise iand function. Now your test and assignment statement may look like this:

 

if (iand(varArg%varVal%vt,VT_R8).gt.0) then
   MayBeWrongDouble = varArg%varVal%dblVal
   …
endif

 

In this case, variable MayBeWrongDouble will always be assigned if the variant contains a reference to a double-precision real. So we have solved the problem of figuring out the type of a Variant, however we still may be assigning the wrong value. This is because a Variant with a VT_BYREF mask does not store its contents inside the Variant structure. What the structure contains is a pointer to a value with a base type indicated by the vt field. This pointer can be accessed using the generic field varArg%varVal%byRef or, for this particular case, the field varArg%varVal%pdblVal. In other words, the assignment

 

MyMayBeWrongDouble = varArg%varVal%dblVal

 

Only works if varArg is not masked with VT_BYREF. If this mask is present, trying to access the referred value using the field dblVal would produce some very odd results.

 

At this point you may be thinking that we are running against a big wall. De-referencing a pointer is a straight-forward operation in C/C++ or Pascal, and some Fortran compilers offer non-standard extensions that allow de-referencing of pointers, but this is not the case for all Fortran compilers. How then do we get the content of a Variant that has been masked with VT_BYREF?. Well, there are three ways to do this. The first involves creating an intermediate Variant using subroutine VariantChangeType (or its extended version VariantChangeTypeEx). The second, and simpler solution uses one of the f90VB’s value-retrieval functions. A third approach involves using subroutine VariantCopyInd and is explained in the next section of this chapter.

 

Subroutine VariantChangeType is one of f90VB’s general-purpose procedures that can be called to change the type of a Variant. For example, you could call VariantChangeType to convert a Variant that contains an integer value into a Variant that contains the same value, now expressed as a real quantity. Here is an example of a call to VariantChangeType:

 

Call VariantChangeType(varSrc, varDest, vtType)

 

Argument varSrc receives a Variant whose type is to be changed, and argument varDest returns a Variant with the new type. vtType is the type to which varSrc should be changed. If varSrc and varDest are set to the same Variant variable, VariantChangeType performs the conversion in place. But you can also pass different variables in varSrc and varDest. During the process of changing the type, VariantChangeType de-references the original Variant if necessary. So varDest always contains a de-referenced version of the original Variant changed to the requested type. Armed with this short explanation of how VariantChangeType works (see the f90VB Reference Manual for a detailed explanation) we can now write the following portion of code to test the type of argument varArg and extract its double-precision value:

 

VarTmp = VariantCreate(iRet)
if (iand(varArg%varVal%vt,VT_R8).gt.0) then
   Call VariantChangeType(varArg, varTmp, VT_R8)
   FinallyAGoodDouble = varTmp%varVal%dblVal
   …
endif

 

Note that we use VariantChangeType to create an intermediate Variant (varTmp) that has the same type as the argument, but whose contents have been de-referenced (if necessary), so we know for sure that the field varTmp%varVal%dblVal contains a double-precision real.

 

Subroutine VariantChangeType works fine for basic Variant types, but there is a situation that will break the code based on VariantChangeType when you try to use it for generalized cases. This is explained in the next section (De-referencing chained Variants).

 

The second approach to solve the same problem uses f90VB’s value-retrieval functions. In this case, the code would look as follows:

 

if (iand(varArg%varVal%vt,VT_R8).gt.0) then
      FinallyAGoodDouble = VariantToDouble(varArg)
      …
endif

 

Function VariantToDouble returns the contents of the passed Variant converted to a double precision value. If the argument has a VT_BYREF mask, then the function takes care of de-referencing the pointer [19]. f90VB provides a full set of functions similar to VariantToDouble that return the content of Variant variables converted to any of the intrinsic types supported by Fortran.

 

De-referencing chained Variants