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

using polymorphic pointer to wrap module

Kabriel
Beginner
713 Views

I tried unsuccessfully to use a polymorphic pointer in a Fortran calling program to represent a specific user defined type in a library that users of the library don't necessarily have access to the module files.

I wrote a Fortran library to handle some empirical data. In the original project, the calling program was written in C. I used modules and then had some specific subroutines that acted as the interface to a C calling program. In essence, C would pass a void pointer to the Fortran library routine that used the iso_c_binding intrinsic module to allocate a pointer of the correct type and then associate the pointer with that pointer. It works really well. However, there is now interest in calling some of the routines through Fortran, and the company distribution program does not handle module files. So, I set out to write a Fortran subroutine wrapper. My original idea was to use a polymorphic pointer, such that a Fortran calling program would not have to know anything about the module in library, something like:

program test
  class(*), pointer :: fp
  call finit(fp)
  call fprint(fp)
end program test

where inside of the library subroutine "finit", the pointer is being allocated to the proper type, something like:

subroutine finit(fp)
  use Point
  implicit none
  class(*), pointer :: fp
  type(TPoint), pointer :: p
  allocate(p)
  call init(p)
  fp => p
  nullify(p)
end subroutine finit

This results in a segmentation fault at the line "fp => p". Inspection with gdb says that the address of fp (0x0) cannot be accessed.

I attached the necessary files to build a simple example, including how I compiled them (see compile.sh). There are three "tests", (1) using the module as if you had access to the mod files - works fine; (2) using the C interface that I talked about above - works fine; and (3) using a subroutine "Fortran" interface to the module - which does not work. The result looks like this:

$./compile.sh 
 test with using module directly
 x, y:   10.00000       10.00000    
 
 test with CInt wrapper
 x, y:   10.00000       10.00000    
 
 test with F wrapper
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source             
a.out              000000000047CC51  Unknown               Unknown  Unknown
a.out              000000000047AD8B  Unknown               Unknown  Unknown
a.out              0000000000447FA4  Unknown               Unknown  Unknown
a.out              0000000000447DB6  Unknown               Unknown  Unknown
a.out              00000000004278A7  Unknown               Unknown  Unknown
a.out              0000000000403370  Unknown               Unknown  Unknown
libpthread-2.23.s  00007EFEFBD5B390  Unknown               Unknown  Unknown
a.out              0000000000402C20  finit_                     24  misc.f90
a.out              0000000000402FA5  test_with_f_wrapp          48  misc.f90
a.out              0000000000403199  MAIN__                     10  main.f90
a.out              00000000004029EE  Unknown               Unknown  Unknown
libc-2.23.so       00007EFEFB9A0830  __libc_start_main     Unknown  Unknown
a.out              00000000004028E9  Unknown               Unknown  Unknown

I have several questions, and am hoping someone might at least be able to point me in the right direction (pun intended). Any thoughts are appreciated.

1. Why does this not work; it seems like this is a good use case for polymorphic pointers.

2. Is there a better way to get around having to send module files.

3. I went so far as to look through the iso_c_binding module and mimicked the code there. Instead of using a polymorphic pointer, this requires using an integer pointer "integer(kind=int_ptr_kind()) :: ptr" and then using compiler directive no_arg_check, which I suspect is suppressing the compiler checker from preventing a compile because it can't find a matching "specific subroutine for this generic subroutine call". This method works but I have never used compiler directives in a production code and am a bit leery of the roubustness of the result -- although I use the iso_c_binding module quite frequently which is doing the same thing. Is this the right way around this problem?

I'm using 17.0.0 20160721 on a Linux system; I have also tested this with 12.1.6 20120928 with same results.

0 Kudos
1 Solution
IanH
Honored Contributor II
713 Views

No files were attached to the original post.

Guessing, but if a procedure has a polymorphic argument, or an argument that is a pointer, it must have an explicit interface.

View solution in original post

0 Kudos
7 Replies
IanH
Honored Contributor II
714 Views

No files were attached to the original post.

Guessing, but if a procedure has a polymorphic argument, or an argument that is a pointer, it must have an explicit interface.

0 Kudos
Kabriel
Beginner
713 Views

File should be there now. I'll try adding the explicit interface.

0 Kudos
Kabriel
Beginner
713 Views

I had to drop this for a bit, but I am looking at it again. I added an explicit interface to the calling subroutine; the error is identical. Specifically, in the attached misc.f90 (attached to original question in example.tar.gz file), I added the interface to the test_with_f_wrapper:

  subroutine test_with_f_wrapper
    implicit none
    interface 
       subroutine finit(fp_)
         class(*), pointer, intent(inout) :: fp_
       end subroutine finit
    end interface
    class(*), pointer :: fp
    nullify(fp)
    call finit(fp)
    call fprint(fp)
  end subroutine test_with_f_wrapper

I am still puzzled why setting the polymorphic pointer in routine "finit" does not work.

0 Kudos
Kabriel
Beginner
713 Views

Curious; if I box the pointer, as suggested by Neil Carlson in this thread, it works, but I don't understand why.

For example, changing this routine in misc.f90 (see original question example.tar.gz):

  subroutine test_with_f_wrapper
    implicit none
    type :: box
       class(*), pointer :: fp => null()
    end type box
    type(box) :: qbox
    call finit(qbox)
    call fprint(qbox)
  end subroutine test_with_f_wrapper

and then modifying the finit, fprint, etc. to operate on a type box, e.g.:

  subroutine finit(qbox)
    use Point
    implicit none
    type :: box
       class(*), pointer :: fp => null()
    end type box
    type(box) :: qbox
    type(TPoint), pointer :: p
    nullify(qbox%fp)
    allocate(p)
    call init(p)
    qbox%fp => p
    nullify(p)
  end subroutine finit

 

0 Kudos
FortranFan
Honored Contributor II
713 Views

Severin K. wrote:

.. I am still puzzled why setting the polymorphic pointer in routine "finit" does not work.

IanH's advice to you in Quote #2 was general, applicable in all situations where the indicated dummy arguments are involved.  You only followed the advice for subprogram finit, now try doing the same with fprint procedure.

0 Kudos
FortranFan
Honored Contributor II
713 Views

By the way, instead of your current file misc.f90 which is an ordinary collection of various subroutines, is it possible for you to 'CONTAIN' them in a module, thereby gain all the benefits the Fortran standard provides with MODULEs including with INTERFACEs, host association, and scope of the IMPLICIT statement?

You can then avoid the issue as in your original post with missing explicit interfaces, but you would need to add a USE statement for such a module in your calling program of course.

module misc_m
! modified misc.f90 file

   use Point
   use CInt
   use iso_c_binding

   implicit none

contains

   subroutine test_with_module
      type(TPoint), pointer :: p
      allocate(p)
      call init(p)
      call print(p)
   end subroutine test_with_module

   subroutine test_with_cint_wrapper
      type(C_PTR) :: cp
      call cinit(cp)
      call cprint(cp)
   end subroutine test_with_cint_wrapper

   subroutine finit(fp)
      class(*), pointer :: fp
      type(TPoint), pointer :: p
      allocate(p)
      call init(p)
      fp => p
      nullify(p)
   end subroutine finit

   subroutine fprint(fp)

      class(*), pointer :: fp
      type(TPoint), pointer :: p
      select type(fp)
         type is (TPoint)
            p => fp
            call print(p)
            nullify(p)
      end select
   end subroutine fprint

   subroutine test_with_f_wrapper
      class(*), pointer :: fp
      nullify(fp)
      call finit(fp)
      call fprint(fp)
   end subroutine test_with_f_wrapper

end module misc_m
program test
   use misc_m
   implicit none
   write(*,*) 'test with using module directly'
   call test_with_module
   write(*,*)
   write(*,*) 'test with CInt wrapper'
   call test_with_cint_wrapper
   write(*,*)
   write(*,*) 'test with F wrapper'
   call test_with_f_wrapper
end program test

 

0 Kudos
Kabriel
Beginner
713 Views

@FortranFan you are right. I didn't look at the error trace close enough. I said "finit" still didn't work, but that was incorrect. In fact, after adding the explicit interface to finit, finit ran fine and the segmentation fault occured in fprint. Adding an explicit interface for both routines works as it should.

To answer your second post, you have to go back to the original question. This example is a simplified code to produce the error that I was seeing. In the actcual program, the issue I have, is that I need a library that can be linked to via Fortran, without access to the module files. I built a C interface module that makes linking to the library via C easy, and I was hoping that I could do something similar via Fortran.

I am glad to understand this a little better than I did. In the end, though, I decided on a completely different approach. The user of the library does not actually need the access to the memory location of the data (what the polymorphic pointer was pointing to). Because of this, I can declare a variable to hold the memory location in the module itself. In the provided example, the module Point would have a variable declared type(TPoint) :: TP for example, and then calls to init and print would not have an argument, but would operate on this module level variable TP. This doesn't make a whole lot of sense with the given example, but it was only an example to illustrate the error that I was seeing in the real code.

Thanks @IanH and @FortranFan.

0 Kudos
Reply