If all you really wish to do is as per your example, then why bother with the Structure on the Fortran-side? The example shown is in fact a "fixed length array" problem, since you are passing "n" to the Fortran s/r explicitly, so Fortan sees it as fixed length (c.f. allocatable etc).
That is, just call a Fortran s/r asking to return a "normal" array, and then assign the array to the VBA Struct after return, or directly via the Arg (e.g. depending if you use strategy 1) or 2) below).
When passing arrays between VBA and Fortran, you have some choices, for example:
1) Pass "normal" arrays, such as:
Apparently, all of the information required on the Fortran side in your example is provided explicitly in the arg list, so why not just
Subroutine ForSub(X,n,Arr)
!
Real(XX), Intent(In) :: X
Integer, Intent(In) :: n
Real(XX), Intent(Out) ;; Arr(n)
!
Arr(1:n) = ....
!
End Subroutine ForSub
The VBA side will require the appropriate declartion of the Sub, and then also a VBA sub which will have a "direct array" syntax, something like
Dim Arr(1 to n)
Call ForSub(X, n, Arr(1))
... etc
Also on the VBA side, with this approach, you will need to then assign the Fortran created Array explicitly to each element of the VBA Struct.
... this is a kind of brute force approach, and though not too efficient on execution, it is easy and reliable (though a bit more of pain on the VBA side for more complex settings).
2) Pass (just the array) as Variant/SafeArray
Alternatively, you can, either as Variant/Array, or a Structure, pass the values. However, as IanH suggests, this requires taking the Args as pointers to the Variant or SafeArray (can use either, though the details vary a little ... I use both Variants and Cray pointer approaches to pass arrays, depending on the specific issues at hand). The full explanation is a rather tedious, but the Fortran side might (depending on the exact approach taken) look like (for simplicity, assuming passing arrays, rather than structs), in this example the Variant is passed in :
!
!************************************************************************
! Basic stats: returns a vector of stats associated with a series X
!************************************************************************
!
!DEC$ ATTRIBUTES DLLEXPORT :: BASICSTATS1D_XL
!
Subroutine BASICSTATS1D_XL(X_VARIANTARRAY, N, RESULTS, IERR, &
ISTATOPTIONX, &
lSTATOPTIONCUMX, &
UPPERQUANTILEX, &
LOWERQUANTILEX, &
LALLOWNONNUMERICX_K2
!
!
! This s/r is provided so that long length data sets can be processed, as opposed to XL 29 limit
! ==> BUT ALSO, to permit stats for seriest that includ #NA's etc, i.e. return stats for
! just the legitimate numeric values in the data set ... if so desired.
!
Use ARTType
Use ARTStats, Only: BasicStats
!
Use ARTF90ExcelMixedLangMod, Only: ARTExcelVarArray1D
!
! Necessary to define interface to SafeArrayxxx calls
!
Use IFCom !Use dfcom in your case
!
Implicit None
!
!
Type(Variant), Intent(In) :: x_VariantArray ! this is an "in" example, but same declaration for "out" also
!
:
:
!
Call ARTExcelVarArray1D(VarArray_Ptr = x_VariantArray%VU%Ptr_Val, n = n, FArray = x(:), iErr = iErr, &
nAllowNonNumericX = nZZ)
!
Here the arg x_VariantArray is declared as a CVF/IVF Variant, and the VBA Variant can be accessed directly. Alternatively, one can send just the pointer (i.e. as a Cray Pointer/Integer) and also access the data that way, but the details are a little different.
Notice that the (custom) s/r ARTExcelVarArray1D() is used to convert the array inside the Variant to a Fortran array. A symmetric s/r converts Fortran arrays to Variants that are Intent(Out), etc. An extremely abridged version (I've removed much of the pre- and post-processing and the substantial amount of "VT testing/coercion" etc) of such an s/r is (this one is for Integer(4)'s):
Subroutine ARTExcelVarArray1D_Int(VarArray_Ptr, n, FArray, iErr, iCoerceX, iFailedCoerceX, iFailedCoerceValX)
!
Use IFCom, Only: SafeArrayGetDim, SafeArrayGetLBound, SafeArrayGetUBound, SafeArrayAccessData, SafeArrayUnAccessData
Use IFWinTY, Only: Variant ! Variant appears to be in IFCom also, so could STREAMLINE
!
Integer, Intent(In) :: n
!
! Declare array_ptr to be a pointer to an integer. When Cray pointers
! are declared, they must point to something; this DUMMY integer is
! simply a placeholder so we can declare the pointer.
!
!
!
Integer :: dummy
!
Pointer (VarArray_Ptr, dummy) ! Can't have Intent() with Ptr's , but this is an (In)
!
Integer, Intent(In), Optional :: iCoerceX, iFailedCoerceX, iFailedCoerceValX
!
Integer, Intent(Out) :: iErr
!
Integer, Intent(InOut) :: FArray(n)
!
!
!
! Locals
! ------
!
! Declare another pointer, which will be used as the head of the
! array of EmployeeInfo's.
!
Pointer (Data_ptr, ArrayData) ! Can't have Intent() with Ptr's, but this Local
!
Type(Variant) :: ArrayData(n)
!
Integer :: i, j, k ! Loop variables
Integer :: iStart, iEnd, nDim
!
Integer :: Status
!
!
iErr = 0
!
FArray(:) = 0
!
!
! Get SafeArray Shape/Extent etc
! ------------------------------
!
!
nDim = SafeArrayGetDim(VarArray_Ptr)
!
!
!
Status = SafeArrayGetLBound(VarArray_Ptr, 1, iStart)
Status = SafeArrayGetUBound(VarArray_Ptr, 1, iEnd)
!
!
! Use the SafeArray routine to get the address of the array of Data
! -----------------------------------------------------------------
!
Status = SafeArrayAccessData( VarArray_Ptr, Data_ptr )
!
If( Status /= 0 ) Then
!
!
iErr = -Status
Go To 99999
End If
!
! Return value Meaning
! -------------------------------
! S_OK Success.
! E_INVALIDARG The argument psa was not a valid safe array descriptor.
! E_UNEXPECTED
!
! ... skip lots of things, for this simple illustration
!
!
Call ARTExcelVarArray1D_Int_XX(ArrayData, n, FArray, iErr, iCoerceX, iFailedCoerceX, iFailedCoerceValX)
!
!
!
99999 Continue
!
!
! When you are done, you must 'deaccess' the data.
! ------------------------------------------------
!
Status = SafeArrayUnaccessData(VarArray_Ptr)
!
If( Status /= 0 ) Then
!
! perform your error handling
!
End If
!
!
End Subroutine ARTExcelVarArray1D_Int
Pure Subroutine ARTExcelVarArray1D_Int_XX(ArrayData, n, FArray, iErr, iCoerceX, iFailedCoerceX, iFailedCoerceValX)
!
Use IFWinTY, Only: Variant ! Variant appears to be in IFCom also, so could STREAMLINE
!
!
Integer, Intent(In) :: n
!
!
!
Type(Variant), Intent(InOut) :: ArrayData(:)
!
Integer, Intent(In), Optional :: iCoerceX, iFailedCoerceX, iFailedCoerceValX
!
Integer, Intent(Out) :: iErr
!
Integer, Intent(InOut) :: FArray(n)
!
!
!
! Locals
!
Integer :: i, iFailedCoerceVal
!
!
!
! VT_Type Verification etc
! ------------------------
!
! Then some elements of the Array are not required type (e.g. Int, Real etc)
!
! Note: requires option for coercion to other Types
! - e.g. if Real(SP) (ie. VT_Type = 2) then maybe OK etc
! - if Int, (i.e. VT_Type = 2 or 3), then what ??
!... skip lot's of things for this simple illustration
!
! First, apply "Error" handling
!
!... skip lot's of things for this simple illustration
! Now, fill in the fields
! -----------------------
!... skip lot's of things for this simple illustration
!
ForAll(i=1:n)
FArray(i) = ArrayData(i)%VU%Long_Val
End ForAll
!
!
!
End Subroutine ARTExcelVarArray1D_Int_XX
In this particular example, the data is passed in as a straight 1-D array (via the Variant). The Variant Type includes various bits of information indicating what it actually contains, and querying those elements is required to determine the specifics of the conversion between Variant's etc and Fortran vars.
I wrote my own Variant/SafeArray/BString etc "converters" (it's quite a bit of work and there are many details), but there are examples in the CVF/IVF package, and on on-line. In general, you will need a collection of such routines to deal with Int/Real/BString 1-D/n-D, etc etc, and lot's of "overloading".
The BString/VBString case is even more tedious, especially if your array is an array of B/VBStrings.
Once you have created explicit (Cray) pointer based vars, and/or explicit Variant vars, stepping through the code with Watch is extremely useful to see how those Types are structured and the information they contain
You may also wish to look at/get a hold of Canaima's F90VB package, which does all this (and much more) for you (though there are slight differences in the internal structure between CVF/IVF Variants and Canaima's F90VB Variants etc).
The syntax on the VBA side is also a little different, depending on whether your VBA vars are declared as Variant, Range, etc
As a matter of interest, passing arrays by this approach executes VERY MUCH faster (primarily since there no need to perform VBA side element-by-element array assignment, and the many other "weird" things that are required on the VBA-side (VBA/COM is not particularly array-friendly), especially with multi dim arr's, Ranges, etc).
The down side is that the coding is much more involved on the Fortran side, and requires a fair amount of pre- and post-process checking for robust implementation.
3) For Typed/Structured Vars
To do this with Structures is just an extension, but with "much more gore". There is an example of one particular way to pass Structure in the CVF package in the dir ... MsDev\DF98\SAMPLES\MIXLANG\VB\TYPEARRAYS, and much discussion on line.
You can do all of this without any ISO binding, if you wish.
0.123456789012346is passed as0x3746F6593FBF9ADDinstead of0x3FBF9ADD3746F659(See binaryconvert.com/…)