2

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.

Sangjun Lee
  • 406
  • 2
  • 12
  • Please don't use the non-standard, non-portable real*8 which is not and has never been part of Fortran. Please see https://stackoverflow.com/questions/838310/fortran-90-kind-parameter . Also could you, in your question, define what you mean by "deep copy" - I don't think you are not using it quite how I think I would use it (which admittedly is rarely) – Ian Bush May 14 '20 at 07:28
  • @IanBush I defined what I meant by 'deep copy' in the question. Thank you for pointing it out. Meanwhile, it's quite a surprise that 'real*8' kind of statement is non-standard since a fairly big CFD code I'm dealing with which is of course constructed by many many experts is using these kind of 'real*8' statement in daily basis. Thank you for the comment, and I will try not to use non-standard as possible. – Sangjun Lee May 14 '20 at 08:27
  • 2
    A deep copy usually means that when a nested structure is copied, a copy is made for every entity that is referenced by the copied structure. It is done recursively. So when, for example, a derived type contains a pointer to an array, a deep copy of such a type would also create a new array and not just copy the pointer. As for the experts, it is often the case that engineering or physics experts are often not experts in Fortran standards. – Vladimir F Героям слава May 14 '20 at 09:03
  • 3
    *which is of course constructed by many many experts*. No, it was probably written over the years by a succession of graduate students who might have been domain experts but were not Fortran experts, nor interested in becoming experts. Such is the case for many scientific codes in widespread use :-) – High Performance Mark May 14 '20 at 09:14

1 Answers1

1

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'.

chw21
  • 7,970
  • 1
  • 16
  • 31
  • This is not true: *"But in both kinds of arrays, the values are directly stored in the memory location that has been assigned to the array (either at program start or when the ALLOCATE is called)."* Allocate typically uses the same allocation for allocatables and for pointers. If you deallocate a variable and allocate again, it may well be allocated elsewhere in the memory. As I showed [recently](https://stackoverflow.com/questions/61776293/difference-in-fortran-pointer-and-fortran-allocatable-in-calling-c-f-pointer) they are typically implemented the same way as pointers. – Vladimir F Героям слава May 15 '20 at 08:04
  • @VladimirF So, ```A2 = A```, and ```A2 = P``` kinds of statement to copy the contents of ```target``` or ```pointer``` into an ```allocatable array``` is valied and safe??It seems work, but refering your answers in https://stackoverflow.com/questions/61776293/difference-in-fortran-pointer-and-fortran-allocatable-in-calling-c-f-pointer, it seems that I'd better give more extra care even though the code compiles and seems okay at the first glance. – Sangjun Lee May 15 '20 at 09:06
  • @SANGJUNLEE The `=` always copies the value, even for pointer vairables. However, Complications arise when you have more complicated structures(derived data types) that contain allocatable and pointer components. For pointer structure components the address is copied and a deep copy is not made. – Vladimir F Героям слава May 15 '20 at 09:08
  • @VladimirF You're right. I'm thinking about how to describe it better. Do you have an idea how I could rephrase that? I don't use allocatable arrays all that differently from static arrays. Pointers I use *very* differently, if I use them at all. Or is the difference more in my head? – chw21 May 16 '20 at 12:35
  • @chw21 No it isn't wrong when reasoning about their most common usage. If you avoid `move_alloc()` and similar and you do not need to reason about the time spent by the allocation. I would just remove the claim about the memory location, Allocatable will find some free space somewhere and place the array data there. – Vladimir F Героям слава May 16 '20 at 12:54
  • Okay, I've removed the paragraph. – chw21 May 17 '20 at 10:32