Calling C code with in-memory data from Fortran

c, c++, fortran, fortran-iso-c-binding, memory-management

Solution

Fortran 2003 introduced C interoperability into the Fortran language. This language feature makes it much easier to write Fortran and C (and hence C++) source that can work together in a portable and robust way. Unless you are prevented from using this level of the language for other reasons, you should very much use this feature.

You have an issue with pointer indirection - whether the pointer to the C++ object is being stored in a long or a pointer to long (the operand to the casts in doSth_ and teardown_A_ should have a * before them). It depends on the C++ and Fortran compilers that you are using, but it is possible that you have a size mismatch between a C long, a C pointer and a Fortran default kind integer.

A modified example showing the approach using Fortran 2003's C interoperability feature below.

// C++
struct A {
    public:
      void do_something()
      {
         // ...
      }
    private:
      // ...
};

// Note no need for trailing underscore.
extern "C" {
    // Note pointer to pointer to void.
    void init_A(void** ptr_ptr_to_A) {
         A* a = new A;
         *ptr_ptr_to_A = reinterpret_cast<void*>(a);
    }

    void doSth(void* ptr_to_A) {
         A* a = reinterpret_cast<A*>(ptr_to_A);
         a->do_something();
    }

    void teardown_A(void* ptr_to_A) {
         A* a = reinterpret_cast<A*>(ptr_to_A);
         delete a;
    }
}


! Fortran 2003
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
  IMPLICIT NONE
  INTERFACE
    SUBROUTINE init_A(ptr_to_A) BIND(C, NAME='init_A')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! This argument is a pointer passed by reference.
      TYPE(C_PTR), INTENT(OUT) :: ptr_to_A
    END SUBROUTINE init_A
    SUBROUTINE doSth(ptr_to_A) BIND(C, NAME='doSth')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! This argument is a pointer passed by value.
      TYPE(C_PTR), INTENT(IN), VALUE :: ptr_to_A
    END SUBROUTINE doSth
    SUBROUTINE teardown_A(ptr_to_A) BIND(C, NAME='teardown_A')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! This argument is a pointer passed by value.
      TYPE(C_PTR), INTENT(IN), VALUE :: ptr_to_A
    END SUBROUTINE teardown_A
  END INTERFACE
  TYPE(C_PTR) :: ptr_to_A
  INTEGER :: i
  !****
  CALL init_A(ptr_to_A)
  DO i = 1, 100
    CALL doSth(ptr_to_A)
  END DO
  CALL teardown_A(ptr_to_A)
END

Problem

I have a complicated C++ object that I'd like to use in my Fortran code. In general , there is no problem in calling C++ code from Fortran (just need to provide a suitable interface with C linkage for instance). However my problem here is that I want my Fortran calls to C++ to operate on what I would call a persistent object: a C++ object created by the first init function, and operated on by other C++ functions. To be more specific, suppose I have the following C++ code ``` struct A { public: void do() { // do something on complicated stuff private: ... // complicated stuff }; extern "C" { void* init_A() { A* a = new A(); return reinterpret_cast<void*>(a); } void doSth(void* ptr_to_A) { A* a = reinterpret_cast<A*>(ptr_to_A); a.do(); } void teardown_A(void* ptr_to_A) { A* a = reinterpret_cast<A*>(ptr_to_A); delete a; } } ``` And the following fortran code (suppose it is main() ): ``` USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR INTERFACE TYPE(C_PTR) FUNCTION init_A() BIND(C, NAME='init_A') USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR IMPLICIT NONE END FUNCTION init_A SUBROUTINE doSth(ptr_to_A) BIND(C, NAME='doSth') USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR IMPLICIT NONE TYPE(C_PTR), INTENT(IN), VALUE :: ptr_to_A END SUBROUTINE doSth SUBROUTINE teardown_A(ptr_to_A) BIND(C, NAME='teardown_A') USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR IMPLICIT NONE TYPE(C_PTR), INTENT(IN), VALUE :: ptr_to_A END SUBROUTINE teardown_A END INTERFACE ``` Now in my real code, this compiles, links, and sometimes works, but sometimes not: it seems like the memory allocated in init_A() is not garanteed to be left unchanged by the Fortran code) I wasn't able to find anything about that on the Internet: - Do you know if there is any standard mechanism to ensure that the memory allocated by init_A_() is left unchanged and still allocated by the fortran code ? - Do you know of any other mechanism that would suit my problem ? Also, can someone explain me why the memory is not managed correctly ? Until now, I thought that Fortran would ask the OS for memory, C++ too, Memory segments given by the OS to both Fortan and C++ were unrelated and garanteed to be non-overlapping, If new memory was asked, the OS would not let Fortran use C++ memory until C++ freed it The C++ memory is freed either by a call to teardown_A() or when the program (i.e. Fortran main) terminates Edit : I updated my code with the answer of IanH, but this is still not working (segfaults, portions of memory are deallocated while calling doSth() from Fortran The original code I posted is the following (for comments refering to it) ``` struct A { public: void do() { // do something on complicated stuff private: ... // complicated stuff }; extern "C" { void init_A_(long* ptr_to_A) { // ptr_to_A is an output parameter A* a = new A(); *ptr_to_A = reinterpret_cast<long>(a); } void doSth_(long* ptr_to_A) { A* a = reinterpret_cast<A*>(*ptr_to_A); a.do(); } void teardown_A_(long* ptr_to_A) { A* a = reinterpret_cast<A*>(*ptr_to_A); delete a; } } ``` And the Fortran code: ``` integer :: ptr_to_A call init_A(ptr_to_A) do i=1,10000 call doSth(ptr_to_A) enddo call teardown_A(ptr_to_A) ```

Original source