Array inside type array as function argument

2019-07-28 08:26发布

问题:

I have the following program at hand

program foo
  type bar
    real, dimension(2) :: vector
  end type
  type(bar), dimension(3) :: bararray
  call doSomething(bararray%vector)
end program

subroutine doSomething(v)
  real, dimension(3,2), intent(inout) :: v
  ...
end subroutine

Now this gives me a compilation error.

Error: Two or more part references with nonzero rank must not be specified at (1)

If I change the call to

call doSomething((/bararray%vector(1), bararray%vector(2)/))

everything works out nicely. The thing is that this just seems a bit cumbersome, so the question is, is there any other way to write the argument for the subroutine?

Thanks in advance.

回答1:

The error arises because there is a constraint (in F2008 the syntax rule is C618) in the language that only one part in a structure component (or similar multi-part reference) may have non-zero rank. Your reference bararray%vector to the structure component has two parts with non-zero rank - the component vector and the variable bararray.

(In the "change the call" approach the references to the component vector have a subscript that gives that part an overall rank of zero, hence bararray%vector(1) is accepted.)

There is a significant latent problem with the "change the call" approach.

The dummy argument in the subroutine is INTENT(INOUT). That requires the actual argument to be definable (a variable that can actually be "varied").

In your "change the call" approach the actual argument associated with that dummy argument is an expression - an array constructor. Expressions are not definable - the result of evaluating them is a value and not something that can be "defined" - i.e. you can't sanely say 2 + 2 = 6.

Presumably in the real code the doSomething subroutine is an external procedure, so your compiler has not diagnosed this. If doSomething had an explicit interface (because it was in a module, perhaps) then I would expect the compiler to report an error.

Others have suggested the approach of re-marshalling your data prior to the call (copy data into an array variable of the appropriate size, call the procedure, copy the data out). Rewriting the interface of the subroutine to take objects of type bar (with the definition of the type moved to a module) is obviously another possibility. Derived types are not only a convenient way of storing data, they are often a convenient way of working with data.

I would be very wary of approaches that attempted to trick the processor into accepting what you "know" to be the layout of the hypothetical array bararray%vector in memory. The layout of types and arrays can change from processor to processor, plus as processor error checking improves that sort of trick may result in later diagnostics. Vendor supplied libraries can get away with that sort of trick, but not us mere programming mortals.



回答2:

Intel Fortran (13.0.xxx) compiler gives the error

error #6159: A component cannot be an array if the encompassing structure is an array.

and I think the answer to your question is that the proper way to call a subroutine which takes a real, dimension(3,2) argument is to give the damn thing a real, dimension(3,2) argument, which would require marshalling your data in some way prior to calling the subroutine. Yes, this is a bit cumbersome, but isn't strict type-checking a nice feature to have ?

You could define a wrapper subroutine along these lines:

subroutine wrapDoSomething(abar)
  type(bar), dimension(:), intent(inout) :: abar
    ...
    ... marshal your data ...
    call subroutine doSomething(marshalled_data)
    ...
end subroutine wrapDoSomething

I suspect that if you define subroutine doSomething without an explicit interface you might get away with passing it what the caller thinks is an array of bars but what the subroutine gets as a rank-2 array of reals. If that doesn't work straight out of the blocks, maybe use BIND(C) on your type definition, which might fool the compiler into allocating contiguous storage for the 6 real numbers.

If it all blows up in your face, you're on your own, my involvement in that mission will be denied.