У меня есть сложный объект C ++, который я хотел бы использовать в своем коде на Фортране.
В общем, нет проблем с вызовом кода C ++ из Fortran (просто нужно предоставить подходящий интерфейс с связью C, например).
Однако моя проблема здесь в том, что я хочу, чтобы мои вызовы Фортрана в C ++ работали с тем, что я бы назвал постоянным объектом: объектом C ++, созданным первой функцией init и работающим с другими функциями C ++.
Чтобы быть более конкретным, предположим, у меня есть следующий код C ++
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;
}
}
И следующий код на Фортране (предположим, это 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
Теперь в моем реальном коде это компилирует, связывает и иногда работает, но иногда нет:
похоже, что память, выделенная в init_A (), не гарантируется, что код Фортрана не останется неизменным)
Я не смог ничего найти об этом в Интернете:
Кроме того, кто-то может объяснить мне, почему память не управляется правильно?
До сих пор я думал, что
Fortran будет запрашивать у ОС память, C ++ тоже,
Сегменты памяти, предоставляемые ОС как Fortan, так и C ++, не связаны и гарантированно не перекрываются,
Если запросить новую память, ОС не позволит Fortran использовать память C ++, пока C ++ не освободит ее
Память C ++ освобождается либо путем вызова teardown_A (), либо когда программа (то есть основная часть Fortran) завершается
Редактировать : Я обновил свой код с ответом IanH, но он все еще не работает (ошибки, части памяти освобождаются при вызове doSth () из Fortran
Исходный код, который я разместил, следующий (для комментариев, ссылающихся на него)
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;
}
}
И код Фортрана:
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)
В Fortran 2003 введена совместимость с Си в языке Fortran. Эта языковая функция значительно упрощает написание исходного кода на языке Fortran и C (и, следовательно, C ++), которые могут работать вместе в переносимой и надежной форме. Если вы не можете использовать этот уровень языка по другим причинам, вам следует очень активно использовать эту функцию.
У вас есть проблема с косвенностью указателя — хранится ли указатель на объект C ++ в long или указатель на long (операнд для приведений в doSth_ и teardown_A_ должен иметь * перед ними). Это зависит от используемых вами компиляторов C ++ и Fortran, но возможно, что у вас есть несоответствие размера между длиной long C, указателем C и целым числом типа Fortran по умолчанию.
Модифицированный пример, демонстрирующий подход с использованием функции взаимодействия на языке Fortran 2003 ниже.
// 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