Passing strings from C++ to Fortran

I know that the topic of string passing has been discussed a lot, but mostly from C++ or C to Fortran, not the other way around. I have not seen the approach below so I want to present it here for discussion. The advantage is that regular Fortran functions can be used unmodified (except for the bind(C) and kind=C_CHAR, which does not really change anything).

Fortran code to be called from C++ or C:

module p

  use, intrinsic :: ISO_C_binding
  implicit none

  contains
  subroutine IO_printCppString(f_str) bind(C, name="F_IO_printCppString")

    character(kind=C_CHAR,len=*), intent(in) :: f_str

    write (*, '(a)') f_str

  end subroutine IO_printCppString

end module p

C++ code:

#include <ISO_Fortran_binding.h>
#include <iostream>

extern "C" {
  void F_IO_printCppString(CFI_cdesc_t* f_str);
}

int main() {
   std::string hello = "Hello World";

   CFI_cdesc_t f_string;
   f_string.base_addr = (void*)const_cast<char*>(hello.c_str()); // void* cast needed? //
   f_string.elem_len = hello.length();
   f_string.rank = (CFI_rank_t)0; // or 1 ? and what about CFI_dim_t in that case //
   f_string.version = CFI_VERSION;
   f_string.type = CFI_type_char;
   f_string.attribute = CFI_attribute_other; // correct? //

   F_IO_printCppString(&f_string);
   return 0;
}

I works fine with gfortran/g++ and ifx/icpx, but when checking with valgrind Intel is not 100% fine. Note that the number of lost bytes does not depend on the string length.

To run

#! /usr/bin/env bash

rm -rf hello p.o hello.o
gfortran -Og -g -fsanitize=undefined -c p.f90
g++ -Og -g -fsanitize=undefined -c hello.cpp
gfortran -fsanitize=undefined p.o hello.o -o hello -lstdc++
valgrind --leak-check=full --show-leak-kinds=all ./hello

rm -rf hello p.o hello.o
ifx -traceback -O0 -c p.f90
icpx -traceback -O0 -c hello.cpp
icpx p.o hello.o -o hello -fortlib
valgrind --leak-check=full --show-leak-kinds=all ./hello

If you are on a Linux system, I would recommend using the ifx (and I presume icpx) LLVM code sanitizer option instead of valgrind. I have had false positives with valgrind on Fortran code and it was usually with character data if I remember correctly. Try compiling with -fsanitize=address option (at least thats what it was the last time Ive tried to use it)

I don’t think you can manually set a descriptors components like that. You’re supposed to use CFI_establish.

Thanks for the suggestions, here is the updated code. Works good:

#include <ISO_Fortran_binding.h>
#include <iostream>

extern "C" {
  void F_IO_printCppString(CFI_cdesc_t* f_str);
}

int main() {
   std::string hello = "Hello World";

   CFI_cdesc_t f_string;
   int i;
   i = CFI_establish(&f_string, (void*)const_cast<char*>(hello.c_str()),
                     CFI_attribute_other,
                     CFI_type_char, hello.length(),
                     (CFI_rank_t)0, NULL);
   F_IO_printCppString(&f_string);
   return 0;
}
#! /usr/bin/env bash

rm -rf hello p.o hello.o
gfortran -Og -g -fsanitize=undefined -c p.f90
g++ -Og -g -fsanitize=undefined -c hello.cpp
gfortran -fsanitize=undefined p.o hello.o -o hello -lstdc++
./hello

rm -rf hello p.o hello.o
ifx -traceback -O0 -fsanitize=address -c p.f90
icpx -traceback -O0 -fsanitize=address -c hello.cpp
icpx -fsanitize=address p.o hello.o -o hello -fortlib
./hello