Reputation: 2758
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:
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)
Upvotes: 3
Views: 594
Reputation: 21451
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
Upvotes: 8