Вызов C из фортрана (ifort, gfortran)

Я программист на C, которому нужно обновить огромную программу на Fortran 2003, добавив единственный вызов функции C.

Во-первых, мне нужно написать минимальную оболочку Fortran (в современном Fortran свободной формы, без криков), которая будет правильно вызывать функцию C со строкой, содержащей счетчик цикла (и дату / время, если возможно), изнутри петля.

Это должно быть «легко», но ни один из выполненных мною поисков не дал достаточно фрагментов для создания работающей программы.

Я использую последние 64-битные версии gfortran и компилятора Intel ifort под 64-битным Linux, и тестовый код необходимо компилировать с использованием обоих компиляторов.

Вот определение C в файле send_to_port.c:

int send_to_port(int port, char *data, unsigned int length);

Последний параметр был добавлен, чтобы позволить Fortran не беспокоиться о конечном нуле (я обрабатываю его в C: data [length] = '\ 0';). Я понимаю, что параметр длины добавляется Fortran "автоматически", поэтому вызов Fortran будет иметь только два параметра: целочисленный номер порта и строку для отправки.

Я надеюсь скомпилировать код со следующей строкой gfortran плюс эквивалент для ifort:

gfortran -ffree-form test.f -o test send_to_port.o

Я ищу минимальный код: думаю, это должно быть около 10-20 строк, но я не знаю Фортрана. Вот мой текущий буфер редактирования для test.f (который не компилируется):

use iso_c_binding
use iso_fortran_env, stdout => output_unit
implicit none

! Fortran interface to C routine:
!   int send_to_port(int port, char *data, unsigned int length);
interface
  integter(c_int) function send_to_port(port, data) bind(C)
  integer(c_int), value :: port
  character(kind=c_char) :: data(*)
end interface

integer(c_int) retval, cnt, port
character(1024) str

cnt = 0
port = 5900

do  ! Infinite loop (^C to exit)
  call fdate(date)
  cnt = cnt + 1
  write(str, "(A,A,I8)") date, ": Iteration = ", cnt
  write(stdout, *) str  ! Show what's about to be sent
  retval = send_to_port(port, str)  ! Send it
  write(stdout, *) retval  ! Show result
  call sleep(1)
end do

end

Помощь?


person BobC    schedule 02.09.2014    source источник
comment
Когда вы используете ISO_C_Binding и вызываете C из Fortran, вы заставляете Fortran генерировать вызов, используя соглашение о вызовах C. Вызов из Fortran, указанный с помощью ISO_C_BINDING, не добавит дополнительных скрытых аргументов, которые могут использоваться в обычном ABI Fortran, но не используются в C ABI. Поэтому не используйте два фактических аргумента в send_to_port и ожидайте, что C увидит три.   -  person M. S. B.    schedule 03.09.2014
comment
Это явно было частью моего замешательства. Когда я просматриваю ссылки из предыдущих поисков, кажется, что интеграция C-Fortran происходила по частям, и я смотрел на код нескольких этапов этой эволюции.   -  person BobC    schedule 03.09.2014
comment
Одно замечание по поводу комментария @ MSB ... оператор USE ISO_C_BINDING не указывает вызов соглашения , что, возможно, подразумевает чтение первого предложения комментария.   -  person IanH    schedule 04.09.2014
comment
Действительно, я написал use с общепринятым англоязычным значением, а не как ключевое слово Fortran use.   -  person M. S. B.    schedule 04.09.2014


Ответы (2)


(Да, это противоречит тегу вопроса fortran-iso-c-binding ...)

Если вы не можете заставить работать iso-c-binding ... В зависимости от версии компилятора, у меня возникли некоторые проблемы, и я предпочитаю работать с металлом при смешивании C и FORTRAN: избегая интерфейсов, просто создайте «оболочку» для вашего C функция.

Примерные рекомендации по оболочке:

  • Имя функции должно заканчиваться на _ (в Linux. В Windows имя функции должно быть ALL_CAPS без завершающего _)

  • Если скомпилирован в программе C ++, определите как extern "C"

  • Все аргументы - указатели

  • В памяти индексы многомерных массивов меняются местами [i] [j] is [j] [i]

Итак, код C будет:

extern "C" 
void send_to_port_fort_(int* port, char *data, int* length, int *result)
{
  *result = send_to_port(*port,data,*length);
}

Тогда из Фортрана

call send_to_port_fort(port,data,size(data),retval)

Поскольку нет инструкции интерфейса, нет ни проверки размера / типа аргумента, ни преобразования

person Blklight    schedule 04.09.2014
comment
Это выглядит одновременно и прагматично, и просто. Я попробую. Решение @IanH, похоже, отбрасывает возвращаемое значение (оно всегда равно нулю, даже если оно не должно быть). Передача возвращаемого значения оболочке C означает, что вызов Fortran будет работать нормально. - person BobC; 05.09.2014
comment
Но я думаю, что я должен использовать len_trim (data) вместо size (data), поскольку send_to_port должен знать длину фактической строки данных, которая (надеюсь!) Меньше размера выделенного пространства. - person BobC; 05.09.2014
comment
Единственное усовершенствование, которое я сделал, - это уменьшение импеданса интерфейса: мои целые числа в Фортране целые (8), поэтому мои целые числа C - int64_t. - person BobC; 09.09.2014
comment
Еще одна проблема: я пытался передать параметр длины 64-битного целого числа как int(len_trim(str),8), но на стороне C. у меня был мусор. Создание переменной integer(8), установка ее на результат len_trim(), а затем передача переменной в функцию C работала нормально. Есть ли у некоторых компиляторов Fortran проблемы с созданием временных файлов правильного типа? - person BobC; 11.09.2014
comment
временные: лично никогда не было проблем. но эмпирическое правило - придерживаться простых переменных и дважды проверять данные на стороне C. - person Blklight; 11.09.2014

В дополнение к комментарию М.С.Б. о количестве аргументов, ваш синтаксис для интерфейсного блока отсутствует, и есть некоторые элементы, которые вам необходимо ввести в область действия тела интерфейса.

Обратите внимание, что * как единица в операторе записи является синонимом (и гораздо более типичным) для OUTPUT_UNIT (и оператор PRINT также записывает в этот модуль - если вам нужен минимальный код). Я также предлагаю TRIM () вокруг элемента вывода str. Точно так же LEN_TRIM (str), вероятно, является разумной длиной для передачи в качестве третьего аргумента в send_to_port.

Не используйте .f в качестве расширения для исходных текстов Fortran в свободной форме - используйте вместо них .f90.

Ваш код содержит ссылку на fdate и sleep, которые не являются стандартными встроенными функциями. Имейте в виду, что их поведение может отличаться в зависимости от ваших компиляторов (я думаю, что вы в порядке, но вы должны проверить. Первый из них, вероятно, можно заменить встроенным DATE_AND_TIME вместе с некоторым подходящим кодом форматирования текста для более переносимого решения.

use, intrinsic :: iso_c_binding, only: c_int, c_char
implicit none

interface
  function send_to_port(port, data, length) bind(C)
    use, intrinsic :: iso_c_binding, only: c_int, c_char
    implicit none
    integer(c_int), value :: port
    character(kind=c_char) :: data(*)
    integer(c_int), value :: length
    integer(c_int) :: send_to_port
  end function send_to_port
end interface

integer(c_int) :: retval, port
integer :: cnt    
character(len=1024,kind=c_char) :: str
character(30) :: date

cnt = 0
port = 5900_c_int

do  ! Infinite loop (^C to exit)
  call fdate(date)
  cnt = cnt + 1
  write(str, "(A,A,I8)") trim(date), ": Iteration = ", cnt
  print *, trim(str)  ! Show what's about to be sent
  retval = send_to_port(port, str, len_trim(str))  ! Send it
  print *, retval  ! Show result
  call sleep(1)
end do
end
person IanH    schedule 03.09.2014
comment
Кроме того, следует ли поместить интерфейс во включаемый файл? Я действительно хотел бы минимизировать изменения, когда я перемещаю этот вызов в существующий код Fortran, поэтому мне интересно, будут ли одна строка включения и один вызов абсолютным минимумом, которого я могу достичь. - person BobC; 03.09.2014
comment
@IanH: Насчет суффикса .f90: не правда ли, что опция компилятора в свободном формате также правильно компилирует код фиксированного формата? Я подумываю включить это по умолчанию, независимо от суффикса файла. - person BobC; 03.09.2014
comment
@IanH: fdate () и sleep () поддерживаются gfortran и ifort, по крайней мере, в 64-битном Linux (вероятно, верно для обоих на всех платформах Intel). Различия в интерфейсе между этими двумя компиляторами значительно уменьшились за последние несколько лет, и gfortran фактически стал лидером по производительности во многих тестах (не знаю о размере кода, использовании памяти или разнице во времени компиляции). - person BobC; 03.09.2014
comment
@IanH: Ошибка: после дальнейшего тестирования выясняется, что значение, напечатанное для retval, всегда равно нулю, даже когда send_to_port () возвращает другие значения. Может быть, это проблема соглашения о вызовах, C против Fortran? - person BobC; 03.09.2014
comment
@IanH: Возврат не происходит с добавлением -lc к строке сборки в моем вопросе или без него. - person BobC; 03.09.2014
comment
Нет - поместите интерфейс в модуль, ИСПОЛЬЗУЙТЕ модуль в области, в которой выполняется вызов функции C. Нет - действительная свободная форма может не быть действительной фиксированной формой, и наоборот - избегать будущего Чего ты думаешь этот человек / я? моменты и правильно назовите свои файлы сегодня. поддержка - это не то же самое, что и достаточно похожая (хотя, по всей вероятности, они достаточно похожи), кроме того, на следующей неделе я слышал, что вы переходите на хфортран и джфорт, где они разные (сон короток для сна с супругом). BIND (C) устраняет различия в соглашениях о вызовах - покажите свой код C и Fortran. - person IanH; 04.09.2014
comment
@IanH: Не могли бы вы обновить свой ответ, чтобы он соответствовал вашему последнему комментарию? - person BobC; 11.09.2014