Deepcopy of a Fortran Pointer

Viewed 326

I want to keep a deepcopy of the resulting fortran pointers from c_f_pointer for check purpose and so on.

(EDIT 1: I used 'deepcopy' as 'making a duplicate which is independent from its original'.

a             = (/1,2,3,4/)
a_deepcopy    = a
a_deepcopy(2) = 1   

In above example, the original a remains (/1,2,3,4/) while a_deepcopy is changed to (/1,1,3,4/). If it was not a deepcopy, changing a_deepcopy's element would also change it's original a's element. I used the word 'deep copy' just becuase I've seen this kind of naming in Python context.)

program test1
    use ...
    use iso_c_binding

    implicit none

    ...

    type(c_ptr)       :: rows_start_c, rows_end_c, col_indx_c, values_c
    integer,pointer   :: rows_start_f(:), rows_end_f(:), col_indx_f(:)
    real*8 ,pointer   :: values_f(:)

    ...


(*) call c_f_pointer(rows_start_c, rows_start_f, [DIM_r])
    call c_f_pointer(rows_end_c  , rows_end_f  , [DIM_c])
    call c_f_pointer(col_indx_c  , col_indx_f  , [rows_end_f(DIM_r)-1])
    call c_f_pointer(values_c    , values_f    , [rows_end_f(DIM_r)-1])

    ...

end program test1

I've googled fairly much to find examples for the deepcopy of fortran pointer to allocatable array, which is the subject that somebody would have questioned already, but couldn't find a proper one.

The following code to copy a pointer to allocatable compiles and works fine so that deepcopy of 'pointer P' is generated in 'allocatable A2' array, but from the answers on my previous questions, I get to know that I'd better be careful treating pointer and allocatable in some mixed way even though they seems to work fine.

program pointer3
implicit none
real*8,allocatable  :: A2(:)
real*8,target       :: A(4)
real*8,pointer      :: P(:)

A = 3.1416d0
P => A

allocate ( A2(size(A,1)) )
A2 = P         # Assign 'pointer' to 'allocatable'

print *, 'Initially'
write(*,'(a, 4f6.2)') 'A ', A
write(*,'(a, 4f6.2)') 'P ', P
write(*,'(a, 4f6.2)') 'A2', A2

P(2) = 1.d0

print *, 'After'
write(*,'(a, 4f6.2)') 'A ', A
write(*,'(a, 4f6.2)') 'P ', P
write(*,'(a, 4f6.2)') 'A2', A2

end program
 Initially
A   3.14  3.14  3.14  3.14
P   3.14  3.14  3.14  3.14
A2  3.14  3.14  3.14  3.14
 After
A   3.14  1.00  3.14  3.14
P   3.14  1.00  3.14  3.14
A2  3.14  3.14  3.14  3.14     # A2 didn't changed after pointer modification, so a deepcopy seems to be generated

So the questions goes,

  1. Is the above code right practice of deepcopying a pointer into a allocatable array?

  2. If I were with ordinary fortran pointer and its target, I would try to deepcopy it to allocatable arrays just assigning its target attribute into the allocatable like below (Even though I can't do this since I'm with c_f_pointer and the equivalent target is c_ptr, which is not a fortran array).

program pointer3
implicit none
real*8,allocatable  :: A2(:)
real*8,target       :: A(4)
real*8,pointer      :: P(:)

A = 3.1416d0
P => A

!allocate ( A2(size(A,1)) )  # Commenting this line does not induce a compile error, as well as a runtime error.
A2 = A                       # Assign 'target' to allocatable

print *, 'Initially'
write(*,'(a, 4f6.2)') 'A ', A
write(*,'(a, 4f6.2)') 'P ', P
write(*,'(a, 4f6.2)') 'A2', A2

P(2) = 1.d0

print *, 'After'
write(*,'(a, 4f6.2)') 'A ', A
write(*,'(a, 4f6.2)') 'P ', P
write(*,'(a, 4f6.2)') 'A2', A2

end program

This works fine as the first try of assigning a pointer right into allocatable array, but this seems weird because apparently 'A', and 'A2' differs on their type: real*8, allocatable and real*8, target.

I think both of above two methods to make deepcopy of a fortran pointer are neither proper, nor safe ones. How can I make a deepcopy of pointer in a safe, and robust way?

Thank you for reading this question.

1 Answers

A word of warning at the beginning: Pointers can be useful, but they are also very dangerous, in the sense that it's very easy to create very obscure and hard-to-debug mistakes with them.

I can't remember all that many cases where I honestly thought: Yes, pointers here are a good idea (though there were some).

Let's start with talking about arrays: Normal arrays have to have their shape declared at compile time, something like this:

INTEGER :: A(10)

This causes the program to set aside an area in memory to store ten integer values.

But you don't always know when coding how large the arrays actually have to be. That's why Fortran introduced allocatable arrays. Instead of declaring their shape at the beginning, the code tells the compiler that it should be prepared that memory will be needed later.

INTEGER, ALLOCATABLE :: A(:)

This tells the compiler that it will need an integer array, but it doesn't know how big it is yet. Then, when the program is running and it has figured out how large an array it needs, you can use the ALLOCATE command to actually create the memory:

ALLOCATE(A(100))

Only after you have called the ALLOCATE on the array can you actually use it. (Actually, there are more reasons to use allocatable arrays, but I won't go into the details yet.)

Pointers are different. They don't store the values, they store a memory address where the value is located. So before you can actually interact with the values, you first need to do one of two things:

  1. Point the pointer to a target, for example:

    P => A
    
  2. Allocate memory that the pointer points to:

    ALLOCATE(P)
    

    You can see that also uses the ALLOCATE command, but it is used slightly differently: When you allocate an allocatable array, you associate the memory that you are allocating directly with the array. When you allocate a pointer, you allocate memory, and then write that memory's address into the memory associated with the pointer.

So with that said, let's go through what you're doing:

A is a more-or-less normal array, except that with the keyword target you've told the compiler that it should be possible to have pointers point at this array. You set all elements of this array to a value.

P is a pointer to an array. You point P at A, which means that these two now work on exactly the same memory. Change one and the other will change as well.

Then you allocate memory for A2. A2 acts as just any other array, it has nothing to do with A or P.

Because P and A refer to the same memory, these two lines have the same effect:

A2 = A
A2 = P

In both cases, the contents of the array A are copied over to the memory of A2. Then, changing any value in A (and it doesn't matter whether you use A or P to do so) will also appear as a change in P. But the values of A2 will not be changed: They are stored somewhere else.

Thinking with pointers is really hard. When I actually have to do this, I most often sketch the process on a piece of paper beforehand, just so that I keep track on where which pointer is pointing at what time.

In Fortran, more often than not you won't need pointers. And without pointers, every copy is what you would call a 'deepcopy'.

Related