Вызов функции C из Фортрана, где имя функции C было первоначально передано из C

По причинам, которые не имеют отношения, мне нужно передать имя функции C / C ++ в подпрограмму Fortran, которая, в свою очередь, вызывает эту функцию C. Я обнаружил, что могу успешно передать имя функции в подпрограмму Fortran. В этой подпрограмме я могу вызвать правильную функцию Си. Тем не менее, аргументы функции C нарушаются при этом вызове (при вызове непосредственно из C он работает нормально). я использовал Связывание ISO C попытаться заставить это работать, но безрезультатно.

Вот MWE:

fortranRoutine.h:

extern "C" {
void fortranRoutine_(void(int status));
};

calledfromFortran.h:

void calledfromFortran(int status);

main.cpp:

#include "fortranRoutine.h"#include "calledfromFortran.h"
using namespace std;

int main(int argc, char** argv) {
calledfromFortran(12);
fortranRoutine_(calledfromFortran);
return 0;
}

fortranRoutine.f90:

subroutine fortranRoutine(calledfromFortran)

use iso_c_binding

implicit none

interface
subroutine calledfromFortran(status) bind (c)
use iso_c_binding
integer(kind = c_int), intent(in) :: status
end subroutine calledfromFortran
end interface

integer(kind = c_int) :: one

one = 2
call calledfromFortran(one)
return

end subroutine fortranRoutine

calledfromFortran.cpp:

#include <iostream>
#include "stdlib.h"
using namespace std;

void calledfromFortran(int status) {
cout << "In calledfromFortran:" << endl;
cout << " status: " << status << endl;
}

Текущие результаты

Запуск этого в настоящее время дает:

In calledfromFortran:
status: 12
In calledfromFortran:
status: -1641758848

Первый звонок calledfromFortran от main работает правильно, но когда он вызывается из fortranRoutine значение сломано. Обратите внимание, что при каждом запуске последнее значение изменяется. Что я делаю неправильно?

2

Решение

При передаче скалярных значений между Fortran и C у вас есть два основных варианта:

  • Вы передаете их по ссылке: на стороне C вы должны убедиться, что вы используете указатели на эти скаляры.

  • Вы передаете их по значению: на стороне Фортрана вы должны убедиться, что вы используете VALUE атрибут, как уже предлагали другие посты.

Для intent(in) атрибут, он может оставаться там, так как не влияет на то, передается ли переменная по значению или по ссылке. В Фортране аргументы всегда передаются по ссылке, если вы не укажете VALUE приписывать. Атрибут intent(in) только говорит компилятору, чтобы он не использовал фиктивный аргумент в подпрограмме, который бы изменил его значение.

Дополнительное примечание по именованию: Вы должны указать свою рутину на Фортране fortranRoutine также с bind(c), Таким образом, вы можете указать его имя, как видно из C, даже если оно находится внутри модуля:

module my_interface
use iso_c_binding

contains

subroutine fortranRoutine(calledFromFortran) bind(c, name='fortranroutine')
...
end subroutine fortranRoutine

end module my_interface

Таким образом, вы можете быть уверены, что имя функции, вызываемой из C, fortranroutineНезависимо от соглашения компилятора добавлять символы подчеркивания, добавлять имена модулей или преобразовывать имена в нижний регистр. Следовательно, у вас будет заголовочный файл на C, который должен работать Компилятор самостоятельно:

extern "C" {
void fortranroutine(void(int status));
};
2

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

Я бы изменил определение интерфейса, чтобы прочитать

interface
subroutine calledfromFortran(status) bind (c)
use iso_c_binding
integer(kind = c_int), VALUE :: status
end subroutine calledfromFortran
end interface

Я не уверен что intent(in) был, но этот фрагмент кода выглядит не правильно. Кроме того, все остальное выглядит (на первый взгляд) разумно и правильно.

Заметка. Привязки ISO C были добавлены только в версии 2003 языка FORTRAN, поэтому, если вы используете более старую версию, возможно, стоит проверить подробности в документации компилятора. Некоторые компиляторы допускают привязки ISO C, но могут вызываться немного иначе, чем я показал выше.


Редактировать. Посмотрев в intent ключевое слово, вы можете попробовать использовать intent(in) в сочетании со следующим объявлением типа, которое следует за interface определение

integer (c_int), parameter :: one = 2

Надеюсь, это поможет.

2

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