Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Pass Pointer Of ALLOCATABLE Array

MarkEH
Beginner
596 Views

I am very new to Fortran but I have been tasked with an interesting problem.

 

For the past months I have been working on splitting a large Fortran application into 2 pieces and I am now looking at passing data between the two halves (call them App1 and App2)

 

Much of the data is in the form of arrays and I have decided to use pointers to get this data across quickly ... this seems to have worked well for fixed length arrays... e.g....

 

In App1....

INTEGER   MYARRAY(10)

INTEGER   MYARRAYLOC

MYARRAYLOC = LOC(MYARRAY)

Pass MYARRAYLOC to App2 as an argument

 

In App2....

INTEGER   MYARRAY2(10)

POINTER (PTR_MA,  MYARRAY2)

PTR_MA = MYARRAYLOC

 

However, if we have....

 

INTEGER,  ALLOCATABLE::MYARRAY( : , : )

INTEGER   MYARRAYLOC

MYARRAYLOC = LOC(MYARRAY)

Pass MYARRAYLOC to App2 as an argument

 

In App2....

INTEGER,  ALLOCATABLE::MYARRAY2( : , : )

POINTER (PTR_MA,  MYARRAY2)

PTR_MA = MYARRAYLOC

 

We get error #7230: An integer pointee cannot have the F90 ALLOCATABLE attribute.

 

Is what I am trying to do even possible? Is there a better\different way to do it?

 

Thank you.

 

0 Kudos
6 Replies
Steve_Lionel
Honored Contributor III
586 Views

You are using an extension often referred to as "Cray pointers". This is not the Fortran standard pointer syntax. However, I don't quite understand what you are trying to do. You don't have two separate applications, just different sets of procedures in one application. Why can't you just pass the array directly?

0 Kudos
MarkEH
Beginner
562 Views

Hi Steve,

 

Thanks for your reply.

 

Yes ... this is the issue ... I want to pass the array from one fortran DLL to another. Furthermore, each DLL has a C wrapper around it ... so passing the array directly "as is" isn't going to work.

 

However .... I've come up with the solution .... I perhaps over thought it....

 

There are many arrays and variables that need passing across the DLL boundary and I've wrapped them all up in a Type ... I simply need to pass the address of the instance of the Type created in App1 across to App2 and it comes through nicely. The Type can contain fixed arrays as well as dynamic arrays.

 

Thanks for the mention of the "Cray pointers" .... this maybe explains why they don't work as well as I'd hoped .... they don't seem to want to use a Target that is housed as a global in an INC file ... so I've been having to work round that which creates a performance hit.

 

I have seen what I assume is a more modern Pointer syntax online so I will look into using that instead.

 

Thanks again for your help.

0 Kudos
Steve_Lionel
Honored Contributor III
549 Views

Modern Fortran has features that can help you, but I don't understand why you can't just pass the array to the C wrapper, which accepts it as a pointer to something and then passes that to the Fortran code.  If nothing else, you can use the C interoperability features, pass C_LOC(array) (which is a C pointer) and that can be passed to the other DLL which calls C_F_POINTER (from intrinsic module ISO_C_BINDING) to "convert" the C pointer (type C_PTR) to a Fortran POINTER to whatever you want, with the bounds you specify.

0 Kudos
MarkEH
Beginner
523 Views

Yes .... I can pass the pointer no problem .... the issue seems to be resolving the pointer to a target in App2 .... it doesn't seem to be happy with Allocatable arrays .... works fine with fixed arrays.

 

Like I say ... I do have a solution ... to wrap everything up (variables, fixed and dynamic arrays, everything) in a Type and pass the pointer of the instance of that... works fine.

 

I'm going to play about a bit more with Pointers.

0 Kudos
FortranFan
Honored Contributor II
508 Views

@MarkEH ,

If you have not tried this already, you may find it helpful to put together minimal working examples toward your specific needs and post them here in case you have any pending questions or issues.

Note with Fortran DLLs on Windows, you don't necessarily need C wrappers though you can include them if there is a need to consume the Fortran code from other platforms such as other C/C++ applications including those from 3rd party, Excel VBA, Microsoft .NET etc. 

On the other hand, you can retain pure Fortran APIs also that can be used by other Fortran DLLs and Fortran main programs.

Toward a minimal working example, if you are unclear how to start with modern Fortran given your comment you're new to Fortran, you can consider the following.

Say your first DLL works with a rank-1 array of integer type, of course it can be any type in actual code, this is just an illustration.  You can include Fortran APIs to update the "data" toward such an array and to access the data with a `pointer` type in order to avoid *copying* the data in caller code from another DLL or EXE, say like so:

module dll_one_m
   private
   integer, protected, allocatable, target, save :: dat_dll_one(:)
   ! Public entities
   public :: update_dat_dll_one, setptr_dat_dll_one
contains
   subroutine update_dat_dll_one()
      dat_dll_one = [ 1, 3, 5 ]
   end subroutine 
   subroutine setptr_dat_dll_one( pdat )
      integer, pointer, intent(inout) :: pdat(:)
      pdat => dat_dll_one
   end subroutine 
end module 

And you may use either Visual Studio or other IDE app or perhaps even the command line to package the above code into a DLL.  Note the DEF file per Microsoft that I recommend:

C:\Temp>ifort /c /standard-semantics dll_one.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.6.0 Build 20220226_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\Temp>link dll_one.obj /def:dll_one.def /out:dll_one.dll
Microsoft (R) Incremental Linker Version 14.32.31329.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library dll_one.lib and object dll_one.exp

* module definition file: dll_one.def

LIBRARY dll_one
EXPORTS
    DLL_ONE_M_mp_UPDATE_DAT_DLL_ONE           @1
    DLL_ONE_M_mp_SETPTR_DAT_DLL_ONE           @2

Now say your second DLL works with a rank-1 array of real type and it consumes the integer array from the first DLL and similar to first DLL, it exposes two APIs, one to compute this DLL "data" and the other to make the data accessible:

module dll_two_m
   use dll_one_m, only : setptr_dat_dll_one
   private
   real, protected, allocatable, target, save :: dat_dll_two(:)
   ! Public entities
   public :: update_dat_dll_two, setptr_dat_dll_two
contains
   subroutine update_dat_dll_two()
      integer, pointer :: pdat_dll_one(:)
      call setptr_dat_dll_one( pdat_dll_one )
      dat_dll_two = [( 0.0, integer :: i = 1, 6 )]
      dat_dll_two(pdat_dll_one) = real( pdat_dll_one )
      pdat_dll_one => null()
   end subroutine 
   subroutine setptr_dat_dll_two( pdat )
      real, pointer, intent(inout) :: pdat(:)
      pdat => dat_dll_two
   end subroutine 
end module 

And the same steps toward packaging:

C:\Temp>ifort /c /standard-semantics dll_two.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.6.0 Build 20220226_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\Temp>link dll_two.obj dll_one.lib /def:dll_two.def /out:dll_two.dll
Microsoft (R) Incremental Linker Version 14.32.31329.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library dll_two.lib and object dll_two.exp

* Similar module-definition file: dll_two.def

LIBRARY dll_two
EXPORTS
    DLL_TWO_M_mp_UPDATE_DAT_DLL_TWO           @1
    DLL_TWO_M_mp_SETPTR_DAT_DLL_TWO           @2

Now you can have a Fortran calling app that consumes both the DLL and the data therein:

   use dll_one_m
   use dll_two_m

   integer, pointer :: pdat_dll_one(:)
   real, pointer :: pdat_dll_two(:)

   call update_dat_dll_one()
   call setptr_dat_dll_one( pdat_dll_one )
   print *, "dat_dll_one = ", pdat_dll_one
    
   call update_dat_dll_two()
   call setptr_dat_dll_two( pdat_dll_two )
   print *, "dat_dll_two = ", pdat_dll_two

   pdat_dll_one => null()
   pdat_dll_two => null()
    
end    
C:\Temp>ifort /c /standard-semantics p.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.6.0 Build 20220226_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\Temp>link p.obj dll_one.lib dll_two.lib /subsystem:console /out:p.exe
Microsoft (R) Incremental Linker Version 14.32.31329.0
Copyright (C) Microsoft Corporation.  All rights reserved.


C:\Temp>p.exe
 dat_dll_one =  1 3 5
 dat_dll_two =  1.000000 0.000000 3.000000 0.000000
 5.000000 0.000000

 

As I mentioned above, this is just an illustration.  You can adapt this toward your needs or put together something else and post here to get your specific questions answered.

And if you do indeed need to work with C wrappers and C callers of Fortran procedures and the data, you can look into interoperability features with C in modern Fortran.

0 Kudos
FortranFan
Honored Contributor II
504 Views

@MarkEH ,

By the way, since you're new to Fortran and may have questions generally about Fortran, particularly those with the current standard - Fortran 2018 - that is supported by Intel one IFORT compiler.  This may be of interest because Fortran 2018 offers many facilities beyond the style you might see in code from the period starting 1950s thru' early 1990s (e.g., with Cray "pointers" above).  If so, you may want to also inquire at the Fortran Discourse and review material available at Fortran-lang.org:

https://fortran-lang.discourse.group/

 

0 Kudos
Reply