У меня есть сложный объект C++, который я хотел бы использовать в своем коде Fortran. В общем, нет проблем с вызовом кода C++ из Fortran (просто нужно предоставить подходящий интерфейс, например, с привязкой к C).
Однако моя проблема здесь в том, что я хочу, чтобы мои вызовы Fortran к C++ работали с тем, что я бы назвал постоянным объектом: объектом C++, созданным первой функцией инициализации и управляемым другими функциями 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 останется неизменным)
В инете ничего по этому поводу не нашел:
- Знаете ли вы, существует ли какой-либо стандартный механизм, гарантирующий, что память, выделенная init_A_(), останется неизменной и по-прежнему будет выделена кодом fortran?
- Знаете ли вы какой-либо другой механизм, который подойдет для моей проблемы?
Кроме того, может ли кто-нибудь объяснить мне, почему память не управляется правильно? До сих пор я думал, что
Fortran запросит память у ОС, C++ тоже,
Сегменты памяти, предоставленные ОС как Fortan, так и C++, не были связаны и гарантированно не перекрывались,
Если запрашивалась новая память, ОС не позволяла Fortran использовать память C++ до тех пор, пока C++ не освобождал ее.
Память C++ освобождается либо вызовом teardown_A(), либо когда программа (т.е. основная Fortran) завершается.
Редактировать: я обновил свой код ответом IanH, но он все еще не работает (segfaults, части памяти освобождаются при вызове 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)
*ptr_to_A = reinterpret_cast<long>(a);
это неопределенное поведение, как и этоA* a = reinterpret_cast<A*>(ptr_to_A);
Ответ @IanH, вероятно, будет правильным, поскольку он использует правильные типы для вещей на стороне фортрана и не вставляет указатели вint
s - person Spudd86   schedule 09.07.2014