Ondřej Čertík wrote:
> I've been asked this question at [1]. In short, what is the native
> Fortran way of declaring/implementing MPI_Send, so that it can accept
> any user derived type or array as the first argument?
MPIv3 has "TYPE(*), DIMENSION(..)" [new in post-F2008's Technical
Specification (TS) 29113], which is additionally currently the only
option to really accept any argument without doing anything special on
the caller side - but it requires some special support by the MPI
library as DIMENSION(..) passes an array descriptor. Advantage is that
it permits to efficiently transport arrays which are noncontiguous. I
think Open MPI has some prep implementation, but no full support. (MPICH
lags a bit behind, at least in the publicly available version.) Compiler
support: To my knowledge, the only compiler which has some support for
it is gfortran 4.8 - but it doesn't use the standardized array
descriptor (yet). [For OpenMPI, some draft implementation for it exists,
which uses gfortran's descriptor.]
Directly compatible with the non-assumed-rank MPI_Send is TYPE(*) and
TYPE(*), DIMENSION(*); however, the former only supports scalars while
the latter only supports arrays (including array sections).
Additionally, to my knowledge only GCC/gfortran 4.8 supports it. In the
next Fortran standard, it will be possible to declare a TYPE(*) which
supports scalars *and* arrays.
MPI implementations try to solve this using a bunch of
pragmas/directives. In case of gfortran, since GCC 4.9 one can use
"TYPE(*), DIMENSION(*)" with "NO_ARG_CHECK" directive. In case of other
compilers, such a pragma also exists (and contrary to gfortran: since
years).
See for instance OpenMPI's
https://svn.open-mpi.org/source/xref/ompi-trunk/ompi/mpi/fortran/base/mpi-f08-interfaces.F90
- which additionally requires that one fills in
"OMPI_FORTRAN_IGNORE_TKR_TYPE" as done by
https://svn.open-mpi.org/source/xref/ompi-trunk/config/ompi_fortran_check_ignore_tkr.m4
> One way to get the job done is by using c_ptr from iso_c_binding,
> along these lines:
That's the other possibility. That should work well, except for two
things: First, it requires that the user always wraps the argument in
C_LOC or C_FUNLOC. And secondly, Fortran 2008 requires that function
pointers are interoperable (restriction lifted in TS29113) and also for
variables there are some restrictions (especially in Fortran 2003;
Fortran 2008 and TS29113 lift some of the restrictions). In case of
gfortran, only GCC 4.9 has updated to the F2008 (and TS29113)
restrictions - and before it even rejected some valid Fortran 2003. The
more checking a compiler provides, the more likely you run into cases
where the compiler rejects an argument to c_loc/c_funloc.
In any case, you have to be careful with arrays: c_loc(array) only takes
the address of the first element into account - that can be a problem
for noncontiguous arrays.
> * what if I don't want to use c_loc(), since my understanding is that
> iso_c_binding should only be used for interfacing C, not for native
> Fortran code.
C_LOC is also fine within pure Fortran code - you don't even need to
declare the function as BIND(C).
* * *
Dan Nagle wrote:
> You might check whether class(*) does what you want.
First, CLASS(*) has the same problem as TYPE(*): It only accepts scalars
(while e.g. CLASS(*), dimension(*) only accepts arrays, including array
elements). Additionally, it has some additional information packed in
the argument [the run-time type information] - similar to an array
descriptor - which makes handling a bit problematic.
[In case of GCC, support for CLASS(*) was only added after TYPE(*)
[still in GCC 4.8] and CLASS with assumed size is not even supported.
Granted, other compilers have a well working CLASS(*) support since
years and but lack TYPE(*).]
* * *
Tom Clune wrote:
> Is it really "bad" to use ISO_C_BINDING for pure Fortran?
No, why should it? It only has two down sides: First, it requires manual
conversion to type(c_ptr) and type(c_funptr). Secondly, some constraints
make it inconvenient to use for certain data types. Although, those are
typically the data types which might also be problematic with MPI_send,
e.g. extensible derived types - in particular with allocatable/pointer
components. TS29113 makes things much simpler with C
binding/ISO_C_Binding - but thanks to assumed type (TYPE(*)) and assumed
rank (DIMENSION(..)), it often is not even needed in a pure Fortran code.
> I have a case where I'm considering using C_LOC and C_F_PROCPOINTER to
> deal with procedures that have varying signatures. My need (as per an
> earlier message on this list) is due to needing to store pointers to
> user-defined procedures within a library. THe usage is always
> consistent and C_LOC provides a robust storage mechanism for the
> intermediate phase.
For proc-pointers it should work. However, I think only TS29913 lifted
the restriction that the argument has to be interoperable. Hence, e.g.
with GCC/gfortran up to 4.7 it will reject procedures which aren't
BIND(C) with C_FUNLOC. [GCC 4.8 accepts it and only 4.9 includes the
full F2008/TS29113 changes for c_loc/c_funloc.]
For C_LOC you will hit similar issues - some variables aren't
interoperable enough for C_LOC and additionally they need the target (or
pointer) attribute.
* * *
Summary:
* As of Fortran 2008/TS29113 there is no really good way for MPI_send;
Fortran 2015 has a means and TS29113 plus MPIv3 offer even a better way.
* Using C binding or directives/pragmas will often work well and is a
good work around, but not fully satisfying and not without issues.
* * *
For MPI: Actually, I wonder why you want to do it manually. MPI
distributions offer modules, which either contain only EXTERNAL
statements, which have no argument checking but usually work. Or they
provide explicit interfaces using a vendor specific way (cf. Open MPI
links above). That approach seems to be way easier than trying to come
up with an own solution.
Tobias
|