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

Unlimited polymorphic and C pointers

Jacob_Williams
New Contributor I
908 Views

I'm trying to create a c_ptr to an allocated class(*),pointer variable, and then convert it back later into a Fortran pointer. Here is a toy example:

program test

    use iso_c_binding

    implicit none

    type :: blah
        integer :: i = 0
    end type blah

    class(*),pointer :: p,q
    type(c_ptr) :: cp

    allocate(p,source=blah(999))
    cp = c_loc(p)

    allocate(blah :: q)      ! (*) either of these
    !allocate(q,mold=blah()) !
    call c_f_pointer(cp,q)

    select type (q)  ! works as long as (*) is present, otherwise crash
    type is (blah)
        write(*,*) 'q%i:',q%i
    end select

end program test

If I don't have the allocate(blah :: q), the c_f_pointer call seems to works, but then it crashes when I try to access anything in the variable. So, my question is: is this the right way to do this? My concern is that it's a memory leak (it seems like the memory grabbed in this allocation would be lost when I call c_f_pointer? But I'm not sure). I'm using ifort 17.0.2.

0 Kudos
7 Replies
Steve_Lionel
Honored Contributor III
908 Views

This will never work. When you do C_LOC, you lose ALL of the extra info that is kept with a polymorphic entity, and there is no way to reconstruct it. At best you could convert back to a pointer of fixed type.

Ideally, the compiler would complain about the use of a polymorphic pointer in the call to C_F_POINTER, but the current implementation doesn't allow for that and the standard doesn't require a check.

 

0 Kudos
Jacob_Williams
New Contributor I
908 Views

So, this would be good?:

program test2

    use iso_c_binding

    implicit none

    type :: blah
        integer :: i = 0
    end type blah

    class(*),pointer :: p
    type(c_ptr)  :: cp
    type(blah),pointer :: q

    allocate(p,source=blah(999))

    cp = c_loc(p)
    call c_f_pointer(cp,q)

    write(*,*) 'q%i:',q%i

end program test2

It seems to run fine.

0 Kudos
Neil_Carlson
Beginner
908 Views

Can C_LOC be applied to a polymorphic variable?  My understanding had been "no",  but looking at the standard just now, I'm not so sure.  If it isn't allowed, you can wrap the pointer in a type, and apply c_loc to a variable of that type.  I've used this trick before.

0 Kudos
FortranFan
Honored Contributor II
908 Views

Neil Carlson wrote:

Can C_LOC be applied to a polymorphic variable?  My understanding had been "no",  but looking at the standard just now, I'm not so sure. ..

The standard revision that is "in the works" i.e., N2118 toward Fortran 2015 which, by the way, strives to include via TS 29113 further (some read this as enhanced) interoperability with C, explains on the dummy argument X in the C_LOC(X) function,

It shall either be a variable with interoperable type and kind type parameters, or
be a nonpolymorphic variable with no length type parameters.

I infer that to mean X can be a polymorphic variable whose dynamic instance is an interoperable type with interoperable kind type parameters; otherwise, X will have to be a nonpolymorphic variable with no length type parameters.

So in the example above from Message #3, if the type of blah were to be interoperable (which it is not, at least in principle), then the C_LOC(p) instruction will be standard-conforming.  That is my quick take on it.

0 Kudos
Neil_Carlson
Beginner
908 Views

What you inferred is exactly what led me to have doubts, but it is an inference and not clearly right (to me at least), though I hope it is.

Boxing the polymorphic pointer has the advantage of preserving the extra metadata that one loses when applying c_loc directly to the pointer. For example

program main

  use iso_c_binding

  type box
    class(*), pointer :: p => null()
  end type
  type(box), target  :: pbox
  type(box), pointer :: qbox
  type(c_ptr) :: cp
  
  allocate(pbox%p, source=1)
  cp = c_loc(pbox)
  call c_f_pointer(cp, qbox)
  
  select type (q => qbox%p)
  type is (integer)
    print *, 'got integer', q
  class default
    print *, 'lost dynamic type'
  end select

end program

 

0 Kudos
Jacob_Williams
New Contributor I
908 Views

The "box" method also works for me (it also has the advantage of also working on gfortran, which the other two do not). So, the theory is that this is OK because box is a nonpolymorphic variable with no length type parameters (even though it does contain a polymorphic variable?)

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
908 Views

FortranFan's "inference" is exactly what the standard intends to say - the argummt shall either be interoperable (which clss(*) is not) or s non-polymorphic variable. Non-standard. As I said, it would be nice if ifort complained about this. I have little doubt that some other compilers do.

0 Kudos
Reply