0

I have C++ code in file test-Q.cpp that calls a Fortran subroutine in file getqpf.F. In file test-Q.cpp, I've declared the Fortran code as external, and I am calling the function using the getqpf_() name-mangling convention. The gcc and gfortran compilers are being used on GNU/Linux.

Here is a snippet from the top of the C++ file:

extern "C" {
            void  getqpf_  (double *tri, 
                    int nsamp, 
                    int lwin,
                    int nfreqfit, 
                    double dt, 
                    float null, 
                    int L2,
                    double df,
                    double *qq, 
                    double *pf, 
                    double *ampls, 
                    double *work1, 
                    double *work2, 
                    double *work3, 
                    double *work4,
                    int mem, 
                    int morder, 
                    int nfs, 
                    double *xReal, 
                    double *xImag, 
                    double *xAbs,
                    double *x1,
                    int cen,
                    int top,
                    int bot, 
                    float cut,
                    int nfst,
                    int raw);  

        } // end

Here is a corresponding snippet from the Fortran file:

   subroutine getqpf (tri, nsamp, lwin, nfreqfit, dt, null, L2, df,
     1                   qq, pf, ampls, work1, work2, work3, work4,
     2                   mem, morder, nfs, xReal, xImag, xAbs, x1,
     3                   cen,top,bot, cut,nfst,raw)



      integer  morder, lwin, nsamp, nfreqfit, delay, nfs

      real     tri(*)
      real     qq(*), pf(*), ampls(*)

      real * 8 work1(*), work2(*), work3(*), work4(*)
      real * 8 xReal(*), xImag(*), xabs(*), x1(*)

      real * 8 dt8, cut8, df8
      real     null, cut
      integer  nfst
      logical  mem, L2, cen, top, bot, raw


      integer nf

C program logic code starts here
          nf = nfreqfit
          delay = 0
          dt8  = dt
          cut8 = cut

The Fortran code calls other C-code functions. On GNU/Linux using the gfortran and gcc compilers I've compiled and linked all of the files in the following manner:

 g++ -c test-Q.cpp -I./boost/boost_1_52_0/ -g
 gcc -c paul2.c -g
 gcc -c paul2_L1.c -g
 gcc -c paul6.c -g
 gcc -c paul6_L1.c -g 
 gcc -c fit_slope.c -g
 gfortran -c getqpf.F -g
 g++ -o test-Q test-Q.o paul2.o paul2_L1.o paul6.o paul6_L1.o fit_slope.o getqpf.o -g

Although I am able to build the binary successfully, there is a segfault that occurs at the line nf = nfreqfit. This is situated at the very top of the Fortran file. Running gdb on the binary produces the following output:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000406fd3 in getqpf (tri=..., nsamp=Cannot access memory at address 0x3e9
) at getqpf.F:44
44        nf = nfreqfit

What is happening here, and why is there a segfault? It appears that memory is not being properly passed between the C++ code and the Fortran code.

UPDATE

As IanH mentions in the answer below, the problem is due to not passing arguments by reference. Using C++, the function must be declared as:

 extern"C" {
            void  getqpf_  (float *tri, 
                    int &nsamp, 
                    int &lwin,
                    int &nfreqfit, 
                    float &dt, 
                    float &null, 
                    int &L2,
                    float &df,
                    float *qq, 
                    float *pf, 
                    float *ampls, 
                    double *work1, 
                    double *work2, 
                    double *work3, 
                    double *work4,
                    int &mem, 
                    int &morder, 
                    int &nfs, 
                    double *xReal, 
                    double *xImag, 
                    double *xAbs,
                    double *x1,
                    int &cen,
                    int &top,
                    int &bot, 
                    float &cut,
                    int &nfst,
                    int &raw);  

        } // end 

Note the presence of the ampersands. Then, the function can be called in the code as:

getqpf_ (tri,       
    nsamp, 
    lwin,
    nfreqfit, 
    dt, 
    null, 
    L2,
    df,
    qq, 
    pf, 
    ampls, 
    work1, 
    work2, 
    work3, 
    work4,
    mem, 
    morder, 
    nfs, 
    xReal, 
    xImag, 
    xAbs,
    x1,
    cen,
    top,
    bot, 
    cut,
    nfst,
    raw); 

Note that variables such as nsamp are declared as int nsamp = 1001.

Nicholas Kinar
  • 1,440
  • 5
  • 24
  • 36

2 Answers2

3

While seconding M.S.B.'s recommendation about using F2003's C interoperability, note that your specific issue is a pass by reference/pass by value mismatch (which is still something that you have to consider even when using C interoperability). Typical Fortran implementations pass all arguments by reference, while in C(++) the default is by value. On the C++ side, note that all of the int and float arguments and some of the double arguments lack the pointer specifier (*). These arguments are passed by value - but there is nothing on the Fortran side to indicate that. Before F2003 this was usually done using compiler specific directives in the Fortran code.

Using F2003's C interop, the default passing convention for arguments to procedures with the BIND(C) attribute is by reference. Arguments that are passed by value need to have the VALUE attribute in their declaration.

IanH
  • 21,026
  • 2
  • 37
  • 59
  • Thanks, IanH! I think this is probably where things are going wrong. What do I need to change in my code to ensure that the crash does not occur? When I declare the function in my extern "C" braces, do all of the arguments have to be a pointer? So how do I call the function? – Nicholas Kinar Nov 16 '12 at 21:30
2

I recommend using the Fortran ISO C Binding. There are examples here on Stackoverflow and in the gfortran manual. It is part of the Fortran 2003 language standard and before that a Technical Report for Fortran 95. That makes it compiler and platform portable. You don't have to worry about compiler specific calling conventions or name mangling.

M. S. B.
  • 28,968
  • 2
  • 46
  • 73
  • Thanks, M.S.B. Does the binding take care of memory-passing issues as well, or is this simply a way to ensure that the subroutine is being called in the proper fashion? – Nicholas Kinar Nov 16 '12 at 17:53
  • I'm also looking for a rather cleancut and to the point example of how to use the Fortran ISO binding. How might I modify the code above? I don't know if I've managed to find a code snippet example similar to the one that I've posted above. – Nicholas Kinar Nov 16 '12 at 18:54
  • If you use the right description of the C routine in Fortran with the ISO C Binding, it causes the Fortran compiler to use calling conventions compatible with the C compiler. The two work together. See the Chapters "Mixed Language Programming" and "Intrinsic Modules", section ISO C Binding of the gfortran manual. – M. S. B. Nov 16 '12 at 19:20
  • Here is a stackoverflow example: http://stackoverflow.com/questions/8207997/calling-a-fortran-subroutine-from-c – M. S. B. Nov 16 '12 at 21:53
  • Thanks, M.S.B. The example shows how to set up ISO binding for characters (strings being passed), but how would I use this for other datatypes (such as the floats and doubles in my code)? – Nicholas Kinar Nov 17 '12 at 00:46