0

I am a fresh in programming, I wanna to call a fortran function in my c++ code. the thing is I dont know how to pass a fortran character*81 array to my c++.

fortran code is like:

subroutine func01(a)
    implicit none
    character*81 a(2)
    write(*,*) a(1)
    write(*,*) a(2)
end

c++ code is like:

#include <iostream>

extern "C"{
    void func01_( const char **a );
}    

int main()
{
    const char *a[2];
    a[0]="Hello world!";
    a[1]="This is a test!";
    func01_(a);
    return 0;
}

I bascially tested my fortran code using this

program pro01
    character*81 a(2)
    a(1)='Hello world!'
    a(2)='This is a test!'
    call func01(a)
end program pro01

'func01(a)' works well.

thanks to @PaulMcKenzie, I corrected some fool problems.....

However, when i compiled cpp code, the result went like messy codes like:

 7
@L
@��n��@�UH�j��FP
 @��n���U�շ�=��U�ྼ���   @��

what should I do?

Kylxyz
  • 29
  • 9
  • 1
    I think you need to review basic C++: `char a[2][81]; a[0][81]="Hello world!";` That is not the way to initialize a char array with a string. C++ is **not** Fortran. – PaulMcKenzie May 24 '15 at 03:13
  • Which tool chain are you using? From the layout, obviously not F77. Up to Fortran 95, there is no C interoperability built in and any interfacing is vendor specific. From Fortran 2003 onwards there is C (not C++) interoperability. – cup May 24 '15 at 05:15
  • Thx @cup ... but, I did not catch you..... what I did is: step1. gfortran -ffree-form -c func01.f ; step2. g++ -std=c++0x -c tstcpp.cpp ; step3. g++ -o tst01 func01.o tstcpp.o; that is all....... – Kylxyz May 24 '15 at 05:22
  • Do an internet search for ISO_C_BINDING. You will get lots of hits and possibly a solution to your problem. – cup May 24 '15 at 06:19

2 Answers2

2

The following code seems to work for gcc4 on Linux(x86_64), but it is not clear whether it is also valid for other platforms. (As suggested above, C-interoperability of modern Fortran may be useful.)

func01.f90

subroutine func01( a )
    character(*) :: a( 2 )
    print *
    print *, "char length = ", len(a(1)), len(a(2))
    print *, "raw a(1) : [", a(1), "]"
    print *, "raw a(2) : [", a(2), "]"
    print *, "trim     : [", trim(a(1)), "] [", trim(a(2)), "]"
end

main.cpp

extern "C" {
    void func01_( char *c, const int len );
}

#include <iostream>
#include <cstring>  // for memset()
int main()
{
    const int lenmax = 30, numstr = 3; // changed char length to 30 to fit in the terminal
    char a[ numstr ][ lenmax ];
    std::string str[ numstr ];

    str[0] = "moon"; str[1] = "mercury"; str[2] = "jupiter";

    for( int k = 0; k < numstr; k++ ) {
        memset( a[k], ' ', lenmax );  // fill space                                              
        str[k].copy( a[k], lenmax );  // copy at most lenmax char (no \0 attached)                        
    }

    func01_( a[0], lenmax );
    func01_( a[1], lenmax ); // pass from mercury
    return 0;
}

Compile

$ g++ func01.f90 main.cpp -lgfortran

Result

char length =           30          30
raw a(1) : [moon                          ]
raw a(2) : [mercury                       ]
trim     : [moon] [mercury]

char length =           30          30
raw a(1) : [mercury                       ]
raw a(2) : [jupiter                       ]
trim     : [mercury] [jupiter]
roygvib
  • 7,218
  • 2
  • 19
  • 36
  • I think character strings are passed as `char*` with their lengths appended as "hidden" variable at the end of the argument list. But this may be system-specific (e.g. when multiple `character` arguments exist), so please check for a more portable way... – roygvib May 24 '15 at 08:35
  • thank u @roygvib, I think your answer is enough for me, thx a lot. – Kylxyz May 24 '15 at 08:38
2

Here is a portable solution to pass an array of arbitrary length strings from C to Fortran.

I used a C++ file very similar to your own:

#include <iostream>

extern "C" void func01(const char **a);

int main()
{
  const char *a[2] = {"Hello World","This is a test"};
  func01(a);
  return 0;
}

The only changes above are the initialization of the character arrays and removing the not-so-portable underscoring of the Fortran function. Instead we will be using standard C interoperability provided by Fortran 2003. The Fortran implementation of func01 becomes:

subroutine func01(cstrings) bind(C,name="func01")
  use, intrinsic :: iso_c_binding, only: c_ptr, c_char, c_f_pointer
  implicit none
  type(c_ptr), dimension(2), target, intent(in) :: cstrings
  character(kind=c_char), pointer :: a1(:), a2(:)

  ! size_t strlen(char * s);
  interface
     function strlen(s) bind(C, name='strlen')
       use, intrinsic :: iso_c_binding, only: c_ptr, c_size_t
       implicit none
       type(c_ptr), intent(in), value :: s
       integer(c_size_t) :: strlen
     end function strlen
  end interface

  call c_f_pointer(cstrings(1), a1, [strlen(cstrings(1))])
  call c_f_pointer(cstrings(2), a2, [strlen(cstrings(2))])
  write (*,*) a1
  write (*,*) a2
end subroutine func01

The bind attribute is what gives us interoperability with C for the function name and we are using C types for variables. The variable cstrings will take an array of 2 pointers, or in C, *[2] or **. The bulk of the procedure is an interface block which lets us call the standard C library routine strlen to make our life easier with the following calls to c_f_pointer which translates a C pointer to a Fortran pointer.

When compiled and run, the output, as expected, is:

$ ./string-array-test
 Hello World
 This is a test

Compiled and tested with gcc 5.1.0.

casey
  • 6,855
  • 1
  • 24
  • 37
  • awesome man! thank you very much! @casey ; like what cup said, is this a kind of iso_c_binding, bcs, i searched iso_c_binding many of the fortran code has things like "bind(C,name="func01")". thank you very much again! cheers.. – Kylxyz May 25 '15 at 03:10
  • @Kylxyz yes, this is modern Fortran iso_c_binding (as noted by using that module in the above code) – casey May 25 '15 at 03:13