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

C++ -> Fortran pass by reference

Simon_Geard
New Contributor I
781 Views

As I understand it pass-by-reference is not supported from C++ to Fortran is not supported, or at least not part of the language. Nevertheless it seems to enable passing a value to a Fortran routine whose argument is not declared to have the 'value' attribute. The code below illustrates this:

#ifdef FORTRAN
subroutine mysub1(i) bind(c,name='mysub1')
    use iso_c_binding, only: c_int
    integer(c_int), intent(in), value :: i
    write(*,'(a,i0)') 'mysub1: i = ',i
end subroutine mysub1

subroutine mysub2(i) bind(c,name='mysub2')
    use iso_c_binding, only: c_int
    integer(c_int), intent(in) :: i
    write(*,'(a,i0)') 'mysub2: i = ',i
end subroutine mysub2

subroutine mysub3(i) bind(c,name='mysub3')
     use iso_c_binding, only: c_int
     integer(c_int), intent(in), value :: i
     write(*,'(a,i0)') 'mysub3: i = ',i
end subroutine mysub3

#else

extern "C" {
    void mysub1(int&);
    void mysub2(int&);
    void mysub3(int);
};

int main (int, char**) {
	int i=3;
	mysub1(i);
	mysub2(i);
	mysub3(i);
	
	return 0;
}
#endif

To use it save to a file e.g. example.txt then on a command line do the following:

ifort /c /fpp /DFORTRAN /Tf example.txt /free /Foexample_f.obj

cl /Tpexample.txt /Feexample.exe example_f.obj

Then run it: example.exe

mysub1: i = 2357888
mysub2: i = 3
mysub3: i = 3

The way mysub2 is declared seems to give the required behaviour but I don't think it's standards conforming. So my questions are:

1) what are the problems with doing this?

2) can the current behaviour be relied upon?

3) is it advisable to convert to the declaration used in mysub3?

 

Thanks.

 

0 Kudos
4 Replies
TimP
Honored Contributor III
781 Views

In your usage with mysub1, your result will vary between 32 and 64 bit modes, assuming 32 bit default integer.  you have c_intptr_t.  As far as I know, Fortran standard doesn't specify anything about c++ reference, but if your c++ follows the usual rules about interoperability between c++ reference and c pointer, you will be ok.

0 Kudos
Steven_L_Intel1
Employee
781 Views

Your understanding is not correct - pass-by-reference absolutely is supported and is what you get if you omit the VALUE attribute from an argument to a procedure with BIND(C).

0 Kudos
IanH
Honored Contributor II
781 Views

I did have a look at one stage through the C++ standard, and, from what I can remember, I don't think there are rules for interoperability between C pointers and C++ references, and there are explicit differences (references may not require storage and you can't have a pointer to a reference).  I think it is a pretty safe assumption that C++ references are implemented using C pointers when they appear in the formal parameter list of the function - it is possible that practical considerations even require that implementation.  However, I personally do not rely on this assumption.

To highlight, this (mysub4) is the example that is missing from the code in #1, that shows conforming "pass by reference" (informal sense - neither Fortran nor C++ formally describe things that way).

#ifdef FORTRAN
subroutine mysub1(i) bind(c,name='mysub1')
    use iso_c_binding, only: c_int
    integer(c_int), intent(in), value :: i
    write(*,'(a,i0)') 'mysub1: i = ',i
end subroutine mysub1

subroutine mysub2(i) bind(c,name='mysub2')
    use iso_c_binding, only: c_int
    integer(c_int), intent(in) :: i
    write(*,'(a,i0)') 'mysub2: i = ',i
end subroutine mysub2

subroutine mysub3(i) bind(c,name='mysub3')
     use iso_c_binding, only: c_int
     integer(c_int), intent(in), value :: i
     write(*,'(a,i0)') 'mysub3: i = ',i
end subroutine mysub3

subroutine mysub4(i) bind(c,name='mysub4')
    use iso_c_binding, only: c_int
    integer(c_int), intent(in) :: i
    write(*,'(a,i0)') 'mysub4: i = ',i
end subroutine mysub4

#else

extern "C" {
    void mysub1(int&);
    void mysub2(int&);
    void mysub3(int);
    void mysub4(int*);
}

int main (int, char**) {
    int i=3;
    mysub1(i);
    mysub2(i);
    mysub3(i);
    mysub4(&i);     // Pass address of i.

    return 0;
}
#endif

 

0 Kudos
Steven_L_Intel1
Employee
781 Views

First of all, Fortran supports interoperability with C, not C++. To the extent that you can make C++ look like C, it works.

Fortran supports interoperability of argument passing two ways. Assuming an interoperable (BIND(C)) interface,  Assuming that you're not declaring a dummy argument of a Fortran kind that triggers passing by "C descriptor" (TS29113 or Fortran 2015), the standard says:

  • any scalar dummy argument with the VALUE attribute is interoperable with the corresponding formal parameter of the prototype,
  • any dummy argument without the VALUE attribute corresponds to a formal parameter of the prototype that is of a pointer type,

It doesn't talk about "pass by reference", but realistically that's what it means by "a formal parameter of the prototype that is of a pointer type".

0 Kudos
Reply