По причинам, которые не имеют отношения, мне нужно передать имя функции C / C ++ в подпрограмму Fortran, которая, в свою очередь, вызывает эту функцию C. Я обнаружил, что могу успешно передать имя функции в подпрограмму Fortran. В этой подпрограмме я могу вызвать правильную функцию Си. Тем не менее, аргументы функции C нарушаются при этом вызове (при вызове непосредственно из C он работает нормально). я использовал Связывание ISO C попытаться заставить это работать, но безрезультатно.
Вот MWE:
extern "C" {
void fortranRoutine_(void(int status));
};
void calledfromFortran(int status);
#include "fortranRoutine.h"#include "calledfromFortran.h"
using namespace std;
int main(int argc, char** argv) {
calledfromFortran(12);
fortranRoutine_(calledfromFortran);
return 0;
}
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
#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
значение сломано. Обратите внимание, что при каждом запуске последнее значение изменяется. Что я делаю неправильно?
При передаче скалярных значений между 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));
};
Я бы изменил определение интерфейса, чтобы прочитать
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
Надеюсь, это поможет.