It works in practice:
! f_get_string.f90
subroutine f_get_string(str) bind(c,name="f_get_string")
use, intrinsic :: iso_c_binding, only: c_char
character(len=:,kind=c_char), allocatable, intent(out) :: str
str = "hello from fortran"
end subroutine
// get_string_main.cpp
#include <iostream>
#include <string_view>
#include <ISO_Fortran_binding.h>
// str is `character(c_char,len=:), allocatable`
extern "C" void f_get_string(CFI_cdesc_t *str);
static auto make_string_view(const CFI_cdesc_t *str) {
return std::string_view{(char *) str->base_addr, str->elem_len};
}
int main() {
// Create space for the Fortran C descriptor
CFI_CDESC_T(0) f_str_desc;
CFI_cdesc_t *f_str = (CFI_cdesc_t *) &f_str_desc;
// Establish that the descriptor is a deferred-length string
CFI_establish(f_str,
nullptr,
CFI_attribute_allocatable,
CFI_type_char,
0, /* elem_len */
0, /* rank */
nullptr);
// Obtain the string from the Fortran routine
f_get_string(f_str);
// Create a C++-17 string-view
auto str = make_string_view(f_str);
std::cout << str << '\n';
// Free up memory of the Fortran string
CFI_deallocate(f_str);
if (f_str->base_addr) return 1;
return 0;
}
$ gfortran-13 -c f_get_string.f90
$ g++-13 -std=c++17 get_string_main.cpp f_get_string.o -lgfortran
$ ./a.out
hello from fortran