3

I am trying to call a Fortran subroutine in C++.

this is the subroutine start:

    subroutine fireballess(ear,ne,parames,ifl,photar,photer)
    integer ne,ifl
    real*4 ear(0:ne),parames(10),photar(ne),photer(ne)

The subroutine works fine in Fortran, but when I try to call it in C++ I get a segmentation fault. Here is my code:

    #include <stdlib.h>
    #include <stdio.h>
    #include <iostream>

    using namespace std;

    extern "C" void fireballess_( double *fear, int fne,double* fparames, nt fifl, double *fphotar, double *fphoter);

    int main(int argc, char ** argv)
    {
            int ne,ifl;
            double *ear;
        double *parames;

        double *photar;
            double *photer;


        parames = new double[9];

            parames[0]=4.3;
           parames[1]=0.23;
           parames[2]=0.5;
           parames[3]=0.5;
           parames[4]=1.5;
           parames[5]=1.;
           parames[6]=1000.;
           parames[7]=2.15;
           parames[8]=3.;

        ne = 3;

            ear = new double[ne];
        ear[0] = 0.;
        ear[1] = 20.;
        ear[2] = 40.;
        ifl=2;

            photar = new double[ne];
            photer = new double[ne];

    // Check on variables initialization

        for (int i=0;i<=2;i++)    cout << ear[i] <<",";
        cout <<"    "<< ne<<"   ";
        for (int i=0;i<=8;i++)    cout << parames[i] <<",";
        cout <<"    "<< ifl <<" "<< photar[0] <<"   "<< photer[0] << endl;

            cout << "Calling a Fortran subroutine" << endl;
            cout << "===============================" << endl;

    // call to the subroutine -->segmentation fault
            fireballess_(ear,ne,parames,ifl,photar,photer);

        for (int i=0;i<=ne;i++){
        cout << "ear = " <<ear[i-1]<< " - "<<ear[i] << endl;
        cout << "photar = " << photar[i] << endl;
        cout << "photer = " << photer[i] << endl << endl;
        }

            delete[] ear;
            delete[] parames;
            delete[] photar;
            delete[] photer;
    }

The program crashes on the call to the subroutine. I am not very experienced in C++ or Fortran coding, so I am not sure about what to do. So far I have checked the format of the variables passed to the subroutine was correct, and it looks so.

Thanks in advance for any help

---------EDIT------ after reading some comments I revised the code as follows, still getting the same segmentation fault error when the routine is invoked:

using namespace std;

extern "C" void fireballess_( std::vector<float> fear, int fne,std::vector<float> fparames, int fifl, std::vector<float> fphotar, std::vector<float> fphoter);

int main(int argc, char ** argv)
{
    int ne,ifl;
ifl=2;
ne = 3;
    std::vector<float> parames = {4.3,0.23,0.5,0.5,1.5,1.,1000.,2.15,3.};
std::vector<float> ear={0,20,40};
    std::vector<float> photar;
    std::vector<float> photer;

    cout << "Calling a Fortran subroutine" << endl;
    cout << "===============================" << endl;
        fireballess_(ear,ne,parames,ifl,photar,photer);

for (int i=0;i<ne;i++){
        cout << "ear = " <<ear[i-1]<< " - "<<ear[i] << endl;
        cout << "photar = " << photar[i] << endl;
        cout << "photer = " << photer[i] << endl << endl;
}

}
4
  • 2
    One major problem is that real*4 is a four-byte floating point type, and the C++ double type is an eight byte floating point type. The C++ type float is a single-precision four-byte type. Commented May 27, 2019 at 10:24
  • 1
    photar = new double[ne]; -- for (int i=0;i<=ne;i++){ -- cout << "photar = " << photar[i] << endl; -- What happens when i is equal to ne? You have an off by one error and an invalid access. A C++ for loop is always suspicious when <= is used as the loop condition, and the variable is used as an array index. Also, you should drop using new[] and start to use std::vector. Commented May 27, 2019 at 10:40
  • 1
    Second, you are passing an int (the ifl) to your routine. Have you verified that sizeof(int) is exactly the byte size of the integers used by your FORTRAN program? If not the same byte size, you are corrupting the program stack. Commented May 27, 2019 at 10:48
  • @Someprogrammerdude I substituted all doubles with floats, and nothing changed :( Commented May 27, 2019 at 11:33

2 Answers 2

2

Combining various info in the comments and the other answer, I guess a minimum-modified version of the original code may look like this:

fortsub.f90

subroutine fireballess(ear, ne, parames, ifl, photar, photer)
    implicit none
    integer ne, ifl
    real*4 ear(0:ne), parames(10), photar(ne), photer(ne)

    print *, "ear     = ", ear
    print *, "ne      = ", ne
    print *, "parames = ", parames
    print *, "ifl     = ", ifl
    print *, "photar  = ", photar
    print *, "photer  = ", photer
    print *
    print *, "sizeof(integer) = ", sizeof(ne)
    print *, "sizeof(real*4)  = ", sizeof(ear(1))
end

main.cpp

#include <iostream>

extern "C"
void fireballess_( float *ear, int *ne, float *parames,
                   int *ifl, float *photar, float *photer );

int main()
{
    std::cout << "sizeof(int)   = " << sizeof(int)   << std::endl;
    std::cout << "sizeof(float) = " << sizeof(float) << std::endl;

    int ne = 3, ifl = 2;

    float *ear = new float[ne + 1] { 0.0f, 20.0f, 40.0f, 60.0f };

    float *parames = new float[10]
        { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f,
          0.6f, 0.7f, 0.8f, 0.9f, 1.0f };

    float *photar = new float[ne] { 1.0f, 2.0f, 3.0f };
    float *photer = new float[ne] { 4.0f, 5.0f, 6.0f };

    fireballess_( ear, &ne, parames, &ifl, photar, photer );

    delete[] ear;
    delete[] parames;
    delete[] photar;
    delete[] photer;
}

Here, the modified parts are: (1) real*4 in Fortran usually corresponds to float in C++; (2) we need to pass the address of actual arguments (particularly for ne and ifl); and (3) ear should have ne + 1 elements because it is defined as ear(0:ne) on the Fortran side. Then, compiling the codes as

$ gfortran-8 -c fortsub.f90
$ g++-8 main.cpp fortsub.o -lgfortran

gives (with GCC8 + MacOS10.13)

sizeof(int)   = 4
sizeof(float) = 4
 ear     =    0.00000000       20.0000000       40.0000000       60.0000000    
 ne      =            3
 parames =   0.100000001      0.200000003      0.300000012      0.400000006      0.500000000      0.600000024      0.699999988      0.800000012      0.899999976       1.00000000    
 ifl     =            2
 photar  =    1.00000000       2.00000000       3.00000000    
 photer  =    4.00000000       5.00000000       6.00000000    

 sizeof(integer) =                     4
 sizeof(real*4)  =                     4

If we use std::vector, the corresponding code may look like this (by looking at some tutorials on std::vector...)

// main2.cpp
#include <iostream>
#include <vector>

extern "C"
void fireballess_( float *ear, int *ne, float *parames,
                   int *ifl, float *photar, float *photer );

using Vecf = std::vector<float>;

int main()
{
    int ne = 3, ifl = 2;

    Vecf ear { 0.0f, 20.0f, 40.0f, 60.0f };

    Vecf parames { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f,
                   0.6f, 0.7f, 0.8f, 0.9f, 1.0f };

    Vecf photar { 1.0f, 2.0f, 3.0f };
    Vecf photer { 4.0f, 5.0f, 6.0f };

    fireballess_( ear.data(), &ne, parames.data(),
                  &ifl, photar.data(), photer.data() );
}

which seems to give the same result (by replacing main.cpp by main2.cpp).

Sign up to request clarification or add additional context in comments.

Comments

1

The modern way of calling Fortran functions from C and C++ code is via ISO_C_BINDING, see its tag wiki on SO. Without this interface there are some quirks like

  • all variables are passed by reference (pointer) to Fortran functions
  • when passing arrays you must also pass their lengths
  • ...

This old quirky way of C/Fortran interoperation is described e.g. here.

Note that you can't pass std::vector or any other complex C++ object to a C or Fortran function and expect that it'll understand it. This will most likely not work, unless the function was designed to handle this. So you need to get actual low-level data (e.g. pointers to arrays) from these objects and pass them instead.

3 Comments

I think my knowledge is way too limited to understand the first part of the answer. Regarding the second part, according to you the problem is I am passing vectors to a fortran function not equipped to handle them. In my first attempt (posted in the question) I tried passing pointers arrays though, with no better results
@J.Dowe if you do post a minimal compilable Fortran program to reproduce the problem (since I don't have any experience writing in Fortran from scratch), I'll try to give a working example.
@Ruslan -- You probably can't pass a std::vector, but you can still use std::vector in the C++ program and pass std::vector::data() to the FORTRAN program. But you are right that the number of entries is totally gone when passing just a buffer -- basically the called function has to assume the buffer is big enough (the documentation to the function would usually specify what the minimum size of the buffer would be, and not have the programmer guess what the size should be).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.