Вызов C-кода с данными в памяти из Фортрана

У меня есть сложный объект 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 (), не гарантируется, что код Фортрана не останется неизменным)

Я не смог ничего найти об этом в Интернете:

  • Знаете ли вы, существует ли какой-либо стандартный механизм, гарантирующий, что память, выделенная 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)

3

Решение

В 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
8

Другие решения


По вопросам рекламы [email protected]