R вызывает функцию Fortran с символьным аргументом - PullRequest
2 голосов
/ 13 октября 2019

Как я могу вызвать из R подпрограмму Фортрана с символьным аргументом? Моя попытка сделать это не работает, хотя я могу вызывать подпрограммы Fortran с аргументами двойной точности. Для кода Фортрана

subroutine square(x,x2)
  double precision, intent(in)  :: x
  double precision, intent(out) :: x2
  x2 = x*x
end subroutine square

subroutine pow(c,x,y)
  character (len=255), intent(in) :: c
  double precision, intent(in)    :: x
  double precision, intent(out)   :: y
  if (c == "s") then
     y = x**2
  else if (c == "c") then
     y = x**3
  else
     y = -999.0d0 ! signals bad argument
  end if
end subroutine pow

и кода R

dyn.load("power.dll") # dll created with gfortran -shared -fPIC -o power.dll power.f90
x <- 3.0
foo <- .C("square_",as.double(x),as.double(0.0))
print(foo)
bar <- .C("pow_",as.character("c"),as.double(x),as.double(0.0))
print(bar)

Выход из

C:\programs\R\R-3.6.1\bin\x64\rterm.exe --vanilla --slave < xcall_power.r

равен

[[1]]
[1] 3

[[2]]
[1] 9

[[1]]
[1] "c"

[[2]]
[1] 3

[[3]]
[1] -999

1 Ответ

3 голосов
/ 13 октября 2019

Когда вы используете .C для вызова подпрограммы Fortran, вызывающий обрабатывает символьные аргументы как стиль C char **. Это несовместимо с фиктивным аргументом Fortran типа character(len=255).

. У вас есть два «простых» подхода:

  • изменить подпрограмму Fortran, чтобы принимать аргументы, похожие на char **
  • используйте .Fortran вместо .C

Изменение подпрограммы Fortran для использования совместимости C с char ** лучше предмет нового вопроса (для его широты ине является специфическим для вашей проблемы R). В общем, я предпочитаю писать процедуры на Фортране, которые будут использоваться в R как раскрывающие совместимый интерфейс C и .C или .Call. С этим можно также прийти к такому выводу.

Даже в документации на R нет оптимизма относительно передачи символьных аргументов с .Fortran:

'. Fortran' пропускает первый (только) символьная строка символьного вектора в виде массива символов C для Fortran: это может быть использовано как 'символ * 255', если его истинная длина передается отдельно. Только до 255 символов строки передаются обратно. (Насколько хорошо это работает, и даже если оно вообще работает, зависит от компиляторов C и Fortran и платформы.)

Вам необходимо прочитать документацию о соглашениях о передаче аргументов, например для gfortran (обратитесь к соответствующей версии, поскольку эти условные обозначения могут измениться).

С .Fortran и gfortran, тогда с процедурой, которая не совместима с C, вам нужно будет пройти "скрытый"msgstr "аргумент к процедуре Фортрана, указывающий длину символьного аргумента. Это верно для символов явной длины (постоянной длины, даже длины-1 или нет) и символов предполагаемой длины.

Для gfortran до версии 7 этот скрытый аргумент имеет целочисленное значение (R). Используя pow вопроса или с аргументом предполагаемой длины, мы можем попробовать что-то вроде

bar <- .Fortran("pow", as.character("c"), as.double(x), as.double(0.0), 255L)

Заметьте, однако, что это не стандартно и по своей природе не переносимо. Действительно, как отмечают комментарии janneb и документация, связанная выше, то, как вы передадите этот скрытый аргумент из R в скомпилированную gfortran процедуру, зависит от версии gfortran, используемой . Использование 255L в конце, вероятно, не сработает после gfortran 7. Вместо этого вам нужно будет передать скрытый аргумент как нечто, совпадающее с integer(c_size_t) (возможно, 64-битным целым числом). Для компиляторов, отличных от gfortran, вам может потребоваться сделать что-то совсем другое.

На самом деле лучше использовать C-совместимую процедуру с аргументом, взаимодействующим с char ** (используя .C) или char [] (используя .Fortran). Как я уже сказал, здесь стоит остановиться на первом варианте, так как он оставляет больше гибкости (например, более длинные символы, больше переносимости и больше символьных аргументов).

...