c++arraysstringfortranlanguage-interoperability

Return a string array from a C++ function to Fortran


I am struggling with filling a string array in a C++ code. Here is what I tried to do:

main.f90:


program main
    implicit none
    character(len=100) arr(3)
    call get_string_array(arr, 100, 3)
    write(*,'(3(a,3x))') arr
end

cpp_func.cpp:


#include <cstring>
#include <algorithm>

using namespace std;

void ConvertToFortran(char* fstring, size_t fstring_len, const char* cstring)
{
    size_t inlen = strlen(cstring);
    size_t cpylen = min(inlen, fstring_len);

    fill(fstring, fstring + fstring_len, ' ');
    copy(cstring, cstring + cpylen, fstring);
}

extern "C"
{
  void get_string_array_(char** arr, int len, int n)
  {
      const char* strings[] = {"duma1", "duma2", "duma3"};
      for (int i = 0; i < n; i++)
          ConvertToFortran(arr[i], len, strings[i]);
  }
}

After compiling the two files as

ifx -c main.f90
icx -c cpp_func.cpp
ifx main.o cpp_func.o

I receive a segmentation fault. Could you please help me how to print array arr properly within main.f90?

*** Edit ***

Any solution which is able to pass a string array from C++ to Fortran would be appreciated.


Solution

  • First, you should use the C binding features of Fortran, to have a more robust code:

    program main
       use iso_c_binding
       implicit none
       character(len=100) arr(3)
       
       interface get_string_array
          subroutine get_string_array(a, len, n) bind(C)
             import c_int, c_char
             integer(c_int), value :: len, n
             character(kind=c_char,len=1) :: a(*)
          end subroutine
       end interface
       
       call get_string_array(arr, 100, 3)
       write(*,'(a)') arr
    end
    

    As you can see the interface of the C routine is described. In particular, len and n have the value attribute, because C passes the scalar objects by value. Regarding the character array, it is described in the interface as a flat 1D array of individual characters, because this is how the C side sees it with the char* declaration.

    On the C side you don't have a lot of changes. char* instead of char**, and &arr[i*len] instead of arr[i]: for, to address the i-th string of the Fortran array, you have to address the (len*i)-th character in C.

    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    void ConvertToFortran(char* fstring, size_t fstring_len, const char* cstring)
    {
        size_t inlen = strlen(cstring);
        size_t cpylen = min(inlen, fstring_len);
        fill(fstring, fstring + fstring_len, ' ');
        copy(cstring, cstring + cpylen, fstring);
    }
    
    extern "C"
    {
        void get_string_array(char* arr, int len, int n)
        {
            const char* strings[] = {"duma1", "duma2", "duma3"};
            for (int i = 0; i < n; i++) {
                ConvertToFortran(&arr[i*len], (size_t)len, strings[i]);
            }
        }
    }