Marshalling an ALLOCATABLE array through IVF's generated COM interfaces

Marshalling an ALLOCATABLE array through IVF's generated COM interfaces

I have this function prototype as given below. It was generated by the COM Server functionality which runs after editing an .HIE file in Visual Studio, which describes the function like this: 

			<METHOD Name="get_Vertices" AUTOID="1" MAGIC="8" NAME="get_Vertices" PROPERTY="TRUE" PROPINTENT="propget">
				<ARGUMENT Name="value" ASSUMEDSHAPE="TRUE" DATATYPE="REAL(4)" INTENT="out" NAME="value" RANK="1" RETVAL="TRUE"/></METHOD></INTERFACE>

And then generates this:

function IIsoSurface9_get_Vertices( ObjectData ,&
         value) result (hresult)
    use IsoSurface9_Types
    implicit none
    type(IsoSurface9_InstanceData) ObjectData
    !dec$ attributes reference :: ObjectData
    REAL(4), intent(out) :: value
    DIMENSION value(1:)
    integer(long) hresult
    ! <B8> DO NOT REMOVE THIS LINE
    ! TODO:  Add implementation

    hresult = S_OK
    ! <E8> DO NOT REMOVE THIS LINE
end function

ObjectData contains a field, ObjectData%Vertices, which is an ALLOCATABLE array. The structure starts like this: 

 type IsoSurface9_InstanceData
        sequence
        ! <B6DEF> DO NOT REMOVE THIS LINE
        !  TODO:  Add fields and remove "dummy"
        real*4,   ALLOCATABLE :: VERTICES(:)

I have to assign the contents of VERTICES to the intent(out) variable VALUE. I am able to populate the VERTICES variable using the "put" method for my property, but I haven't figured out how to get it to go the other direction.

How is that done? 

 

14 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

No one? I really don't know how to do this. 

It's not clear to me what you're asking. I will note that allocatable and deferred-shape arrays are not really something you can use in COM (I think), since they are Fortran and implementation specific.

Retired 12/31/2016

It's the Fortran COM Server project type; the code was generated by saving off the HIE file. It supplied me with a dummy variable:

REAL(4), intent(out) :: value
DIMENSION value(1:)

Note that the Intel code generated that. I'm being presented with a Fortran interface and a lot of generated code to do the marshalling.

What I have is a REAL(4),  ALLOCATABLE :: Vertices(:) array. We both know that boils down to a region in memory with single precision values therein, one after another. But I'm not such a wizard with Fortran vectors that I know how to turn the one into the other. 

 

 

The point being, simply, that 

VALUE = VERTICES

...didn't work. Specifically, the VALUE variable carries an initial value of "Undefined Pointer/Array", and my question might boil down to how to set up the array, when IVF didn't generate it with a POINTER or ALLOCATABLE attribute.

VALUE is a dummy argument - given that it's not ALLOCATABLE, any storage for it is allocated in the caller. I guess that the Fortran COM layer converts the incoming argument from the COM call to this deferred-shape array, but that would require that the storage for the argument be already defined. It would be inappropriate for it to have ALLOCATABLE, I think, though I suppose that's not out of the question. (If it were, then you'd need to compile with /standard-semantics to get automatic allocation on the assignment.)
 

Retired 12/31/2016

Yes. Value is clearly a dummy argument. 

How does the code generator function? What does it expect that matches with the generated marshalling code? 

Is there any solid documentation about this project type and how it functions? (I can't find any)

The code that is generated attempts to map COM object types to Fortran types, doing conversions in the required directions. We created this for CVF many, many years ago and while it still works, it hasn't had more than bug fixes done to it.  There is actually quite a bit of documentation on it - look for "COM server" in the index.

Retired 12/31/2016

I don't think the generated code is correct for an assumed shape array. I can't tell where it takes the (out) parameter and assigns it into the SafeArray structure. It appears to just call my UI<class>.f90 routine and not use the result. It generates this:

 function $IIsoSurface9_get_Vertices( pInterface ,&
             value ) result (hresult)
        !dec$ attributes stdcall :: $IIsoSurface9_get_Vertices
        use IsoSurface9_Types
        use ifcom
        implicit none
        type (IIsoSurface9_Ptr) pInterface
        !dec$ attributes reference :: pInterface
        integer(INT_PTR_KIND()), intent(out) :: value 
        !dec$ attributes reference :: value 
        integer(long) hresult
        integer i
        REAL(4) f$value(:)
        volatile f$value
        pointer f$value
        integer(INT_PTR_KIND()) ptr$value
        integer(INT_PTR_KIND()) sa$value
        type(T_SAFEARRAY) sad$value
        pointer(ptrsad$value, sad$value)
        type(T_SAFEARRAYBOUND) sab$value(1)
        pointer(ptrsab$value, sab$value) 
        sa$value = NULL 
        
        hresult = SafeArrayAllocDescriptor(1, sa$value)
        if (hresult < 0) goto 9999
        ptrsad$value = sa$value
        sad$value % cbElements = 4
        ptrsab$value = loc(sad$value % rgsabound(1))
        sab$value(1) % cElements = 10
        sab$value(1) % lLbound = 1
        hresult = SafeArrayAllocData(sa$value)
        if (hresult < 0) goto 9999
        hresult = SafeArrayAccessData(sa$value, ptr$value)
        if (hresult < 0) goto 9999 
		

        hresult = IIsoSurface9_get_Vertices(pInterface % pInternalData % pInstanceData ,&
             f$value )
        
        if (hresult < 0) goto 9999
    9999  continue  ! Cleanup code
        
        if (sa$value /= NULL) i = SafeArrayUnaccessData(sa$value)
        value = sa$value 
    end function

Note the last six lines of code do nothing with f$value. I can't tell if it's supposed to do anything at all with f$value, but my sense it that it was supposed to assign it into the SafeArray structure...? And the structure itself gets a data size of 10 rather than trying to measure the array. 

The documentation is not extensive enough, honestly. It doesn't describe everything you see on the screen while editing the HIE file, specifically, there is no discussion about assumed shape arrays. 

So, I can probably gen the interface and then drop the HIE file from the project, since it's letting me manipulate the generated code between edits of the HIE file. But I still can't figure out how to get a pointer to my ALLOCATABLE array, and then get that in turn into the SafeArray structure's pvData field. 

How is that done?

 

With great potential for error (for some reason the letters C, O and M in that order give me the creeps...) the docs say...

An array is always passed as a SafeArray ByRef. Therefore it must be defined with Intent set to InOut.

You've got your array as "out, retval".

If you have the array as per the documentation, you'll see lots of arcane wizardry associated with the generated procedure creating a descriptor to pass to the procedure that you write.

Perhaps this creates more problems - because, from inspection, I think you then need to work out how to get the incoming safe array that gets associated with the assumed shape argument in the procedure that you write to be the right size.  That's easy enough to do if you can edit the generated code that says "Do Not Edit", but something about that gives me a bad feeling. 

Anyway, outside of the COM server stuff, creating a SafeArray that returns an arbitrary sized (or even arbitrary dimensioned) array is easy enough.

I'm probably talking through my hat.  Given that, plus me exceeding my internal limit for the number of "$" signs read in a single source file, I'll go away...

I don't know about your hat. What I understand about COM interfaces is that if you're specifying a property, you match separate get_ and put_ methods with the same DISPID, setting the one to [propget] and the other to [propput]. The interface behaves properly only if the [propget] method's argument is flagged [out, retval].

Clearly, the method creates the SafeArray in memory when it's called; that's also a COM convention; the called method allocates for the caller. That part isn't a problem. The problem is getting the SafeArray set up. If you know how, given a source array that's already set up and just needs to be copied in, then please tell me!

Well, emphasising that I really know jack about COM and even less about the COM server wizard, so perhaps this is irrelevant and misses the point, the attached extract shows how to create, size and copy real data to a safe array.  I copy element by element, I suspect there are ways and means of doing this in bulk.  How to integrate this with the COM server wizard output ... I very much have No Fortran Idea.

 

Attachments: 

AttachmentSize
Downloadapplication/octet-stream safe array extract.f9011.04 KB

Ian, I was able to study your code and figure it out. 

Steve, the bottom line is that there is some automation in that HIE editor which generates *some* code to marshal a SAFEARRAY structure, but, the code it generates doesn't populate the SAFEARRAY structure at all! So the fact that you can define an array parameter in the HIE editor is misleading, as features go. 

I consider that a significant bug, which I can only get around by dropping the HIE file from the project and hand-editing the generated code to do what it should. 

Can you show a run through the steps in the HIE editor to get the interface that is a problem? I can probably take it from there.

Retired 12/31/2016

Leave a Comment

Please sign in to add a comment. Not a member? Join today