Fortran interoperability with C pointer struct - pointers

I have a commercial C library which I want to use with Fortran. There are two functions and a pointer struct like:
struct env;
typedef struct env *ENVptr;
the two functions have the prototype:
ENVptr open(int *status_p);
int close(ENVptr **env_p);
I wrote an Fortran interface to access them:
interface
function c_open(status) bind(c, name='c_open')
use, intrinsic :: iso_c_binding
integer(kind = c_int) :: status
type(c_ptr) :: c_open
end function
function c_close(env) bind(c, name='c_close')
use, intrinsic :: iso_c_binding
type(c_ptr) :: env
integer(kind = c_int) :: c_close
end function
end interface
I use this code to use this in my Fortran program:
type(c_ptr) :: env = c_null_ptr
env = c_open(status)
if ( status .ne. 0 ) then
print *, 'Could not open environment'
stop
end if
...some more code...
if ( c_associated(env) ) then
status = c_close(env)
if ( status .ne. 0 ) then
print *, 'Could not close environment.'
end if
end if
but when I execute the program, I get an Segmentation fault error when the program reaches the c_close function.
Is this the right way to interface the C routines?

I don't see how your program could have linked because the binding name of a procedure has to agree with the name in the actual C prototype. I suppose you could square the names up with a *.def file. Also Fortran has a concept of argument keywords so it is good practice in my opinion to make the Fortran dummy arguments in the interface agree with their documented argument names. Other than that you seem to have the right level of indirection in your interface bodies, so my version would be:
interface
function c_open(status_p) bind(C,name='open')
use, intrinsic :: iso_c_binding
implicit none
type(c_ptr) :: c_open
integer(kind = c_int) status_p
end function c_open
function c_close(env_p) bind(c,name='close')
use, intrinsic :: iso_c_binding
implicit none
integer(c_int) c_close
type(c_ptr) env_p
end function c_close
end interface
Now, there is a problem with the level of indirection when you invoke c_close because the C typedef for ENVptr already makes it a pointer, so ENVptr** envp is a pointer to a pointer to a pointer. In your Fortran code you are passing a c_ptr that points at an opaque type by reference, so you are passing a pointer to a pointer. Thus you need to create an extra level of indirection to make it fly. I would accordingly attempt to modify your code to something like:
type(c_ptr) :: env = c_null_ptr, envpp = c_null_ptr
target env
integer(c_int) status
env = c_open(status)
if ( status .ne. 0 ) then
print *, 'Could not open environment'
stop
end if
!...some more code...
if ( c_associated(env) ) then
envpp = c_loc(env)
status = c_close(envpp)
if ( status .ne. 0 ) then
print *, 'Could not close environment.'
end if
end if
I can't test this obviously but at this point it's syntactically correct and has the right level of indirection according to my reading of your problem statement.

Related

How to have one Fortran function with different type of parameters using c_ptr?

I read the section Type Casting in Callbacks of the article Fortran Best practices.
I would like to use in my program something as described in Using type(c_ptr) Pointer
But I have a problem. I give an outline of what I try to do. I hope it will be sufficient to understand. Otherwise, let me know, I will post a full example.
I have two types and I call the same subroutine with one or the other type. The first parameter of my subroutine is an integer to indicate which is the type of the second parameter (type1_t or type2_t)
type type1_t
real :: a, b
integer :: c
end type1_t
type type2_t
real :: a,e
integer :: b,c,d
end type2_t
type(type1_t) :: type1
! ... init of type1
type(type2_t) :: type2
! ... init of type2_t
call myfoo(1,c_loc(type_1))
call myfoo(2,c_loc(type_2))
But now, I have a problem with the declaration in myfoo because the declaration must be done in fortran before instructions.
I know that the following code does not work :
subroutine myfoo(i, params)
integer, intent(in) :: i
type(c_ptr), intent(in) :: params
if (i == 1) then
type(type1_t), pointer :: pars
elseif (i ==2) then
type(type2_t), pointer :: pars
endif
call c_f_pointer(params, pars)
! do some stuff with pars (there are common parts of code either the dummy args is type_1 or type_2). For example, the line above.
end subroutine myfoo
If I use a block construct, I will have a problem because the variable disappears at the end of the block.
How can I solve it using c_ptr?
A simple way to accomplish this is to put type-specific code in two separate routines in a module, and bind them using an interface, so the compiler will pick the right subroutine based on the type of the variable provided on input:
module blabla
private
public :: foo
interface foo
module procedure foo1
module procedure foo2
end interface
contains
subroutine foo1(params)
type(t1) :: params
! Do cool stuff
end subroutine foo1
subroutine foo2(params)
type(t2) :: params
! Do type-2 cool stuff here
end subroutine foo2
end module blabla

"C pointer trickery" to allow mismatched Fortran array ranks

I'm writing a HDF5 wrapper subroutine that will read/write a double precision array of any shape from/to a dataset inside a HDF5 file. To achieve this, I use some C pointer trickery such that the subroutine takes in only the first element of the array as val, but it actually reads/writes the whole array using the temporary buffer buf(1:sz_buf).
So far I have the following for the read subroutine (after removing error checks to keep it concise):
SUBROUTINE hdf5_read_array_d( fname, path, name, val, dims )
USE ISO_C_BINDING, ONLY: C_SIZE_T, C_LOC, C_F_POINTER
! Input arguments
CHARACTER(LEN=*), INTENT(IN) :: fname, path, name
REAL(KIND(1.D0)), TARGET, INTENT(OUT) :: val
INTEGER, DIMENSION(:), INTENT(IN) :: dims
! Internal variables
INTEGER(KIND=HID_T) :: h5root, h5path, h5dset
INTEGER(KIND=HSIZE_T), DIMENSION(SIZE(dims)) :: h5dims
REAL(KIND(1.D0)), DIMENSION(:), POINTER :: buf
INTEGER(KIND=C_SIZE_T) :: sz_buf
INTEGER :: dim
! Open the file in read-only mode
CALL h5fopen_f( TRIM(fname), H5F_ACC_RDONLY_F, h5root, ierr )
! Open the pre-existing path in the file as a group
CALL h5gopen_f( h5root, TRIM(path), h5path, ierr )
! Open the dataset
CALL h5dopen_f( h5path, TRIM(name), h5dset, ierr )
! Convert dims to HSIZE_T
h5dims(:) = dims(:)
! C pointer trickery: cast double -> void* -> double*
sz_buf = PRODUCT(dims)
ALLOCATE( buf( sz_buf ) )
CALL C_F_POINTER( C_LOC(val), buf, (/sz_buf/) )
! Read data from dataset through buffer
CALL h5dread_f( h5dset, H5T_NATIVE_DOUBLE, buf, h5dims, ierr )
! Clean up and close HDF5 file
NULLIFY(buf)
CALL h5dclose_f( h5dset, ierr )
CALL h5gclose_f( h5path, ierr )
CALL h5fclose_f( h5root, ierr )
RETURN
END SUBROUTINE hdf5_read_array_d
Now, the question is, do I need to also put in DEALLOCATE(buf) in addition to / in place of the NULLIFY(buf)?
Any help would be appreciated.
Note: I am aware that Fortran 2018 includes assumed-rank arrays val(..) that will elegantly solve this problem. But again, it's a newer feature that might not be implemented by all compilers yet.
Edit: On C_F_POINTER(), here's a screenshot of Metcalf, Reid, and Cohen (4th Edition, not the newest one that has Fortran 2018 stuff):
You can use C-style pointer trickery to do what you want, but you have some things to address in your approach:
you have a memory leak with allocate(buf)
you are (subtly) lying about the scalar nature of val
you'll horribly confuse anyone reading your code
The reason why this is horribly confusing, is because you don't need to do this trickery. That's also why I won't show you how to do it, or to address the question "do I need to deallocate as well as nullify?".
You know that you have an array val to stuff n values in, in a contiguous lump. You worry that that can't do that because you (without using an assumed-rank dummy) have to match array shape. Worry not.
integer :: a(2,2,2,2), b(4,2,2), c(4,4)
are all arrays with 16 elements. So is
integer :: d(16)
You can associate actual arguments a, b and c with dummy argument d. Let's see that in action:
implicit none
integer :: a(2,2,2,2), b(4,2,2), c(4,4)
call set_them(a, SHAPE(a))
call set_them(b, SHAPE(b))
call set_them(c, SHAPE(c))
print '(16I3)', a, b, c
contains
subroutine set_them(d, dims)
integer, intent(in) :: dims(:)
integer, intent(out) :: d(PRODUCT(dims))
integer i
d=[(i,i=1,SIZE(d))]
end subroutine
end program
You can even associate array sections in this way to define portions.
You can see several other questions around here about this sequence association, in particular looking at changing shapes of arrays. This answer is more of a motivation of what to look for when tempted to do something complicated instead.

Pointers to arrays as member variables in Fortran derived type

In Fortran, it is not possible to make a member variable of a derived type a target. (I guess this has to do with the standard not specifying how a derived type is stored in memory?) However, I can have a pointer as a member variable and associate pointers with pointers. Like I do in the example below.
module DataMod
type DataType
real(8), private, dimension(:,:), pointer, contiguous :: A
real(8), private, dimension(:,:), pointer, contiguous :: B
integer :: n
contains
procedure :: alloc
procedure :: set
procedure :: print_
final :: dealloc
end type DataType
interface DataType
procedure :: NewDataType
end interface DataType
contains
function NewDataType(dimension_) result(new)
integer, intent(in) :: dimension_
type(DataType) :: new
new%n = dimension_
end function NewDataType
subroutine alloc(dataObject)
class(DataType) :: dataObject
allocate(dataObject%A(dataObject%n,dataObject%n))
allocate(dataObject%B(dataObject%n,dataObject%n))
end subroutine alloc
subroutine set(dataObject, datas, choice)
class(DataType) :: dataObject
real(8), dimension(dataObject%n,dataObject%n), intent(in) :: datas
character(len=1), intent(in) :: choice
real(8), dimension(:,:), pointer :: dataPointer
integer :: i,j
if(choice .eq. 'A') then
datapointer => dataObject%A
elseif(choice .eq. 'B') then
datapointer => dataObject%B
else
stop
endif
do j = 1,dataObject%n
do i = 1,dataObject%n
datapointer(i,j) = datas(i,j)
enddo
enddo
end subroutine set
subroutine print_(dataObject)
class(DataType), intent(in) :: dataObject
print *, 'A'
print *, dataObject%A(1:dataObject%n,1:dataObject%n)
print *
print *, 'B'
print *, dataObject%B(1:dataObject%n,1:dataObject%n)
end subroutine print_
subroutine dealloc(dataObject)
type(DataType) :: dataObject
deallocate(dataObject%A)
deallocate(dataObject%B)
end subroutine dealloc
end module DataMod
program DataTest
use DataMod, only: DataType
implicit none
real(8), dimension(2,2) :: testArray
type(DataType) :: testType
testType = DataType(2)
call testType%alloc()
testArray(1,1) = 1
testArray(2,1) = 2
testArray(1,2) = 3
testArray(2,2) = 4
call testType%set(testArray, 'A')
testArray(1,1) = 5
testArray(2,1) = 6
testArray(1,2) = 7
testArray(2,2) = 8
call testType%set(testArray, 'B')
call testType%print_()
end program DataTest
In the set routine, I use an if statement to set a pointer to decide if it should dump the incoming matrix in A or B. In the program I'm currently working on, I must decide which combination of four different matrices to multiply together and setting a pair of pointers is much nicer than writing 16 almost identical calls to dgemm.
My question is if there are any problems with this approach besides the normal dangers of dangling pointers etc. or a way to do this without pointers? The arrays should not be accessed from outside the object. Are there any performance issues?
Components in a type definition may not be declared with the TARGET attribute (beyond the missing syntax, that would be inconsistent with other concepts and rules in the current language), but if a variable of derived type has the TARGET attribute, then all of its subobjects have the TARGET attribute too. For a type definition:
type DataType
real(8), private, dimension(:,:), allocatable :: A
real(8), private, dimension(:,:), allocatable :: B
...
The procedure set could be written...
subroutine set(dataObject, datas, choice)
class(DataType), TARGET :: dataObject
real(8), dimension(dataObject%n,dataObject%n), intent(in) :: datas
character(len=1), intent(in) :: choice
real(8), dimension(:,:), pointer :: dataPointer
! require dataobject%A and ..%B to already be allocated.
if(choice .eq. 'A') then
datapointer => dataObject%A
elseif(choice .eq. 'B') then
datapointer => dataObject%B
else
stop
endif
datapointer = datas ! or some other operation.
...
(dataPointer could be declared contiguous, the allocatable arrays that it gets pointed at are always contiguous.)
An actual argument without the TARGET attribute may be associated with a dummy argument with the TARGET attribute. If this is the case, then pointers associated with the dummy argument become undefined when execution of the procedure completes. (Such pointers may also become undefined in some cases, even when the actual argument has the TARGET attribute - see F2018 15.5.2.4p8 on for details - but these cases don't apply to scalars.)
Consequently, in the general case, if a pointer to one of the components of an object of derived type needs to outlive a procedure like set above (e.g. if dataPointer was not local to set) and you can't ensure that the actual argument will always have the TARGET attribute, then the original method using pointer components may be more appropriate. The implementation in the question appears to be ok - though I would suggest making the finalizer IMPURE ELEMENTAL to make things more robust to future changes.

Fortran assignment overload with pointers

I want to overload the assignment for a type that I want to use for polymorphic pointers. I dont know the actual subtype the pointer is holding on runtime.
But the following example code reproduces the strange compiler error I get:
module example
type :: base_class
real(4) :: some_garbage
contains
end type
type, extends(base_class) :: sub_class
real(4) :: even_more_garbage
contains
end type
type :: main_operations_t
class(base_class), pointer :: basic_pointer
class(base_class), pointer :: pointer_array(:)
contains
procedure :: main_operations
end type
interface assignment(=)
module procedure assign_base_class
end interface
contains
subroutine assign_base_class(result_object, input_object)
implicit none
class(base_class), pointer, intent(out) :: result_object
class(base_class), pointer, intent(in) :: input_object
result_object%some_garbage = input_object%some_garbage
end subroutine
subroutine main_operations(this)
implicit none
class(main_operations_t) :: this
class(base_class), pointer :: hack
allocate(this%basic_pointer)
allocate(this%pointer_array(2))
this%basic_pointer%some_garbage = 0.0
this%pointer_array(1)%some_garbage = 1.0
this%pointer_array(2)%some_garbage = 2.0
this%basic_pointer = this%pointer_array(1)
this%pointer_array(1) = this%pointer_array(2)
this%pointer_array(2) = this%basic_pointer
this%basic_pointer = this%pointer_array(1)
hack => this%pointer_array(1)
hack = this%pointer_array(2)
hack => this%pointer_array(2)
hack = this%basic_pointer
end subroutine
end module
When I try to assign to the indexed pointer array i.e
this%pointer_array(1) = this%pointer_array(2)
this%pointer_array(2) = this%basic_pointer
I use gfortran 4.8.4 on Ubuntu.
I get a compiler error:
Error: Variable must not be polymorphic in intrinsic assignment at (1) - check that there is a matching specific subroutine for '=' operator
The assignment to the 0d pointer however works without complaints.
The section with the "hack" pointer shows a possible workaround to get this to work in an ugly manner.
The compiler is complaining it needs a defined assignment. You do have one, but it requires pointers:
subroutine assign_base_class(result_object, input_object)
class(base_class), pointer, intent(out) :: result_object
class(base_class), pointer, intent(in) :: input_object
(It is not necessary to repeat implicit none in all module procedures. I'd argue it is a clutter which harms readability.)
And your variables are not pointers. pointer_array(1) is NOT a pointer even though pointer_array is a pointer.
A solution is to remove the unneeded pointer attribute:
subroutine assign_base_class(result_object, input_object)
class(base_class), intent(out) :: result_object
class(base_class), intent(in) :: input_object
result_object%some_garbage = input_object%some_garbage
end subroutine
This compiles cleanly.
It would make sense to have the pointer attribute there if you were doing pointer assignment, but as it stands, there is no use of it in the defined assignment.

fortran 90 recursive subroutines pointer

I have a subroutine that I would like to be called recursively, but it seems that there is a memory leak when calling it. I've already posted questions on memory leaks in functions
Fortran 90 function return pointer
Fortran 90 difference between compaq visual fortran and gfortran
but now I have a new problem. I've switched all of my functions to subroutines, and now I'm having trouble with calling a subroutine recursively. Here is my code (sorry it's not short)
recursive subroutine myCurl3DRTest(d3,u,v,w,gd,n)
implicit none
type(vecField3D),intent(inout) :: d3
real(dpn),dimension(:,:,:),intent(in) :: u,v,w
type(griddata),intent(in) :: gd
integer,intent(in),optional :: n
type(vecField3D) :: temp1,temp2,temp3,temp4,temp5,temp6
real(dpn),dimension(:,:,:),allocatable :: dwdy,dvdz
real(dpn),dimension(:,:,:),allocatable :: dwdx,dudz
real(dpn),dimension(:,:,:),allocatable :: dvdx,dudy
real(dpn),dimension(:,:,:),allocatable :: curlx,curly,curlz
integer :: dummyN
integer,dimension(3) :: s
s = shape(u)
if (.not.present(n)) then
dummyN = 1
else ; dummyN = n ;endif
call nullifyField(temp1)
call nullifyField(temp2)
call nullifyField(temp3)
call nullifyField(temp4)
call nullifyField(temp5)
call nullifyField(temp6)
call myDel(temp1,w,gd,1,2)
call myDel(temp2,v,gd,1,3)
call myDel(temp3,w,gd,1,1)
call myDel(temp4,u,gd,1,3)
call myDel(temp5,v,gd,1,1)
call myDel(temp6,u,gd,1,2)
allocate(dwdy(s(1),s(2),s(3)))
allocate(dvdz(s(1),s(2),s(3)))
allocate(dwdx(s(1),s(2),s(3)))
allocate(dudz(s(1),s(2),s(3)))
allocate(dvdx(s(1),s(2),s(3)))
allocate(dudy(s(1),s(2),s(3)))
call getY(temp1,dwdy)
call getZ(temp2,dvdz)
call getX(temp3,dwdx)
call getZ(temp4,dudz)
call getX(temp5,dvdx)
call getY(temp6,dudy)
call deleteField(temp1)
call deleteField(temp2)
call deleteField(temp3)
call deleteField(temp4)
call deleteField(temp5)
call deleteField(temp6)
call setX(d3, dwdy - dvdz )
call setY(d3,-( dwdx - dudz ))
call setZ(d3, dvdx - dudy )
deallocate(dwdy)
deallocate(dvdz)
deallocate(dwdx)
deallocate(dudz)
deallocate(dvdx)
deallocate(dudy)
allocate(curlx(s(1),s(2),s(3)))
allocate(curly(s(1),s(2),s(3)))
allocate(curlz(s(1),s(2),s(3)))
call getX(d3,curlx)
call getY(d3,curly)
call getZ(d3,curlz)
if (dummyN.gt.1) then
call myCurl3DRTest(d3,curlx,curly,curlz,gd,dummyN-1)
endif
deallocate(curlx)
deallocate(curly)
deallocate(curlz)
end subroutine
And in the main program, I have
do k=1,10**4
call myCurl3DRTest(f3,u,v,w,gd,1)
! call myCurl(f3,u,v,w,gd)
enddo
This, as I mentioned in my previous questions about memory leaks, is causing a memory leak too. Is there something I'm forgetting to allocate? Or are the curlx, curly and curlz not getting deallocated from each level?
Just so it's clear, inside deleteField:
subroutine deleteField(this)
implicit none
type(vecField3D),intent(inout) :: this
if (associated(this%x)) deallocate(this%x)
if (associated(this%y)) deallocate(this%y)
if (associated(this%z)) deallocate(this%z)
this%TFx = .false.
this%TFy = .false.
this%TFz = .false.
end subroutine
and inside nullifyField:
subroutine nullifyField(this)
implicit none
type(vecField3D),intent(inout) :: this
nullify(this%x); this%TFx = .false.
nullify(this%y); this%TFy = .false.
nullify(this%z); this%TFz = .false.
this%TFNullified = .true.
end subroutine
Any help is greatly appreciated!
I'm guessing based on your previous posts that in your setX (and similar) procedures that you allocate some pointer components of the d3 object passed as the first argument. I don't see how those pointer allocations could be matched by a deallocate before the next iteration of the loop or before the nested call to myCurl3DRTest. Depending on what you are trying to do those deallocations could perhaps be provided by calls to nullifyField.
If my guess is right then without those deallocations, you would have a memory leak.
A language level of Fortran 95 plus the allocatable TR would make your life so much easier...

Resources