-2

I have a fortran module, which is part of a c++ program. This module has a global variable, which is a 3d array, and which I would like to have pointed to a 3d Tensor I am defining in c++:

test.cpp:

#include <Eigen/Dense>
#include <unsupported/Eigen/CXX11/Tensor>

extern "C" {
  void f_link_global_variable(double* data, int* dims, int* strides);
}
int main(){
  Eigen::Tensor<double, 3> tensor(1,2,3);
  tensor.setZero();
  tensor(0,0,0) = 1;
  std::array<int, 3> dims = {tensor.dimension(2), tensor.dimension(1), tensor.dimension(0)};
  int n_dims = 3;
  std::cout << "tensor before passing: " << tensor << std::endl; // prints 1 0 0 0 0 0

  f_link_global_variable(tensor.data(), dims.data(), &n_dims);
  std::cout << "tensor after passing: " << tensor << std::endl;
}

stub_fortran_interface.f90:

module fortran_testmodule
  use iso_c_binding
  implicit none
  double precision, pointer :: my_global_ndarray(:,:,:)

contains

subroutine link_global_variable(c_pointer, dims, n_dims) bind(C, name="f_link_global_variable")
  type(c_ptr), intent(in) :: c_pointer
  integer(c_int), intent(in) :: dims(*)
  integer(c_int), intent(in) :: n_dims
  integer, allocatable :: shape_(:)
  integer :: i
  
  allocate(shape_(n_dims))
  do i = 1, n_dims
    shape_(i) = dims(i)
  end do

  call c_f_pointer(c_pointer, my_global_ndarray, shape_)
  ! generates segfault:
  print *, "array in fortran:", my_global_ndarray
  my_global_ndarray(1,1,2) = 3
end subroutine link_global_variable

end module fortran_testmodule

I am linking these files with

enable_language(Fortran)
add_library(stub_fortran_interface SHARED stub_fortran_interface.f90)
target_compile_options(stub_fortran_interface PUBLIC "-fvisibility=default")

add_executable(test test.cpp)
target_link_libraries(test_cpp_fortran_interface
                      PUBLIC
                      stub_fortran_interface)

When I try to print the newly allocated public array in fortran, I get a segmentation fault. Why am I not able to access the values I am defining in my c++ code? Note that the c++ Eigen library uses by default the same col-major format as Fortran, so the issue is not related to the alignment of the array values.

Since this code uses both fortran and c++, I am unable to provide working code in a compiler explorer, so I would be grateful if you could let me know if there is anything that is not working properly.

Yes
  • 339
  • 3
  • 19
  • 1
    `void f_datatypes_test(` ? typo? – 463035818_is_not_an_ai May 23 '23 at 18:01
  • `n_dims` from C++ is given by address, and it seems the Fortran expects an `ìnt` value. You can try to remove the `*` before `strides` and the `&` before `n_dims` in the C++ code. And a check displaying data sent from C++ and data received in Fortran can help to debug. – dalfaB May 23 '23 at 18:34
  • 1
    @dalfaB unless specified otherwise with the `value` attribute, all arguments in Fortran are passed by reference, so this one is fine. – PierU May 23 '23 at 19:45

1 Answers1

1

When you pass a pointer from C/C++, what you get on the Fortran side is the pointed object, not the pointer itself. This is inherent to the way Fortran is passing argument, always by reference (contrary to C that passes by value).

And you don't need the shape array.

So on the Fortran side you should have rather:

subroutine link_global_variable(my_global_1darray, dims, n_dims) bind(C, name="f_link_global_variable")
  real(c_double), intent(in) :: my_global_1darray(*)
  integer(c_int), intent(in) :: dims(n_dims)
  integer(c_int), intent(in) :: n_dims   ! is necessarily 3
!...
my_global_ndarray(1:dims(1),1:dims(2),1:dims(3)) => my_global_1darray(1:prod(dims))

Note that your approach with type(c_ptr) would work if you were adding the value attribute to the argument, meaning that you want to receive the pointer itself.

subroutine link_global_variable(c_pointer, dims, n_dims) bind(C, name="f_link_global_variable")
  type(c_ptr), intent(in), value :: c_pointer
PierU
  • 1,391
  • 3
  • 15