2

Reading a code, I somehow encountered a rather strange grammar on iso_c_binding, and its minimal view is like below.

(Fortran part)

program testF
  use iso_c_binding
  implicit none
  interface
    subroutine get_value_array(arr, num) bind(C)
      use iso_c_binding
      implicit none
(*)   integer(C_INT), dimension(:), pointer, intent(in) :: arr
      integer(C_INT), value, intent(in)                 :: num
    end subroutine
  end interface

  integer(C_INT),dimension(:),pointer :: array
  integer(C_INT) :: n

  n = 7
  allocate(array(n))

  call get_value_array (array, n)

  print *, 'array'
  print *, array

end program

(C++ part)

#include <iostream>

extern "C" void get_value_array(int **in, int num) {
  int i;
  for (i = 0; i < num; i++) {
    (*in)[i] = i+1;
  }
}

After compiling with commands like below,

icc -c testC.cpp
ifort -lstdc++ -o test testF.f90 testC.o
rm *.o

The code actually works and shows the desired output.

 array
           1           2           3           4           5           6
           7

My question is all about the line I marked with (*) at the Fortran part of the code. To learn iso_c_binding stuff, I've searched pretty tones of webpages, stackoverflow literatures, and so on. However, I've never seen that kind of statement where integer(C_INT) + dimension(:) + pointer appear in one line. Acoording to the code, it should mean 'A pointer to int * '.

But you know, in iso_c_binding context, integer(C_INT) alone means int * in C++ perspective, while integer, dimension(:), pointer means 'a pointer to an integer array' in Fortran perspective. Now that they are mixed in one line, it is really confusing. Can anybody clarify how come they get to mean 'A pointer to int * '?

One more, in (*) I only wrote intent(in). In my experience, if I write only intent(in) where intent(inout) is needed, I fail to compile it with following error

error #6780: A dummy argument with the INTENT(IN) attribute shall not be defined nor become undefined.

But I get no error with this example, and I get desired intent(inout) like behavior. Is this some kind of magic with iso_c_binding where intent restrictions are relieved a little bit, or is it just an undefined behavior where I luckily get the desired functionality?

Sangjun Lee
  • 406
  • 2
  • 12
  • This is not the ancient Fortran 90, no reason for that tag. – Vladimir F Героям слава Sep 14 '20 at 13:51
  • It is indeed peculiar to have `dimension(:), pointer` in a `bind(C)` procedure. It probably works by accident for simple contiguous arrays. – Vladimir F Героям слава Sep 14 '20 at 13:58
  • Further to Vladimir F's comment, Fortran doesn't define how such a program behaves, because the Fortran procedure interface is not interoperable with the defined C++ function prototype. – francescalus Sep 14 '20 at 14:15
  • Note also that "A dummy argument with the INTENT(IN) attribute shall not be defined nor become undefined." applies to nonpointer dummy arguments. Pointer dummy arguments with `intent(in)` are prohibited from appearing in pointer association contexts but not from appearing in variable definition contexts (see questions/answers on intent). Finally, `intent(in)` is a promise you make to the compiler, not one the compiler makes to you. – francescalus Sep 14 '20 at 17:56
  • @francescalus You saying 'the Fortran procedure interface is not interoperable with the defined C++ function prototype' is because the C++ function has ```int **``` argument? If so, does it mean that Fortran cannot use C++ functions which take ```double pointer```s as their arguments in a safe, standard way? If I nonetheless want to use that kind of C++ functions in Fortran, could you give me one possible workaround to make it work in a safe way? (not like the luck dependent way as I did in my question) – Sangjun Lee Sep 15 '20 at 01:56
  • 1
    One can write a Fortran interface compatible with an `int **` parameter (see [https://stackoverflow.com/q/61993778/3157076], for a `char **` example). And one can write a C prototype compatible with an `integer(c_int), pointer :: arr(:)` argument. It's just that the pairing you quote isn't a valid one but may have been a non-portable hack used before Fortran improved its interoperability. – francescalus Sep 15 '20 at 08:04
  • Is there any reason to declare "arr" as a pointer and not simply as allocatable? When I write Fortran code I avoid allocate pointers as they cannot be deallocated. Would the declarations on the C++ side change if it is allocated? – Bo Sundman Sep 15 '20 at 09:26
  • @BoSundman As far as I know, using allocatables with ```iso_c_binding``` is rather brand-new feature which was not present until Fortran 2008. Indeed I prefer to use allocatables when possible, and most of my web searching is to find the way of using allocatables with ```iso_c_binding```, where I ended up reaching the Stack Overflow page https://stackoverflow.com/questions/34561835/pass-arrays-from-c-c-to-fortran-and-return-a-calculated-array/34562060. If you can tell me an easy way of playing with allocatables with ```iso_c_binding``` it will greatly help me. – Sangjun Lee Sep 15 '20 at 12:17
  • 1
    There's been a fair amount of discussion in comments now, so maybe you can clarify what you are after? I see three possible answers. 1) The example code isn't Fortran compliant (for reasons); 2) How to write C++ to take a Fortran pointer; 3) How to write Fortran to send to a C++ `int **`. Which of those is the one you want, if it's not a more general discussion? (For 2, you may find [this other question](https://stackoverflow.com/q/63412077/3157076) relevant.) – francescalus Sep 15 '20 at 15:54
  • @francescalus My goal is to use C++ function which takes ```int **```, ```double **``` and so on in my Fortran code, so it'll be 3). And I could find relevant information in the page you mentioned in your earlier comment. Thank you! (https://stackoverflow.com/questions/61993778/how-to-declare-pointer-of-pointer-using-iso-c-binding), thank you :) – Sangjun Lee Sep 16 '20 at 00:43

0 Answers0