Совместимость C и Fortran для строк

Я пытаюсь вызвать некоторый код Fortran из C, но я не нашел правильного способа передачи массива символов C.

   SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
     USE ISO_C_BINDING
     IMPLICIT NONE
     CHARACTER*(C_CHAR)      c_message
     CHARACTER*(256)         f_message
     CALL C_F_POINTER( C_LOC(c_message), f_message)
     WRITE(*,*) f_message,LEN(f_message)
   END

BIND(C) заставляет параметр c_message иметь размер 1. Как я могу получить доступ к другим элементам строки c_message?

Компилятор: GCC 4.8.2


person C. Rossignon    schedule 20.12.2016    source источник
comment
Я смущен. Fortran 77 (и Fortran 90) не имеет функции взаимодействия F2K C. Каким образом ваш код Fortran 77?   -  person John Bollinger    schedule 20.12.2016
comment
Я тоже в замешательстве. Gfortran компилирует код как Fortran 2008, а интерфейс bind(C) — это Fortran 2003.   -  person Vladimir F    schedule 20.12.2016
comment
Вы должны показать подпрограмму, с которой вы взаимодействуете, и сообщения об ошибках, которые вы получаете. Это очень важно.   -  person Vladimir F    schedule 20.12.2016
comment
Например, CHARACTER*(C_CHAR) синтаксически неверно. Вы должны просто использовать CHARACTER(kind=C_CHAR) и не заботиться о том, является ли ваш код F77 или F чем-то еще. Большинство кодов Fortran 77 являются допустимыми кодами Fortran 2008.   -  person Vladimir F    schedule 20.12.2016
comment
Вы правы, gfortran использовал стандарт f2008 и не имеет ISO_C_BINDING со стандартом f95.   -  person C. Rossignon    schedule 20.12.2016
comment
Итак, можем ли мы действительно смешивать C и Fortran 77 без ISO_C_BINDING?   -  person C. Rossignon    schedule 20.12.2016
comment
Я во многом согласен с тем, что говорят люди, но ответить @VladimirF: CHARACTER*(C_CHAR) правильно. Это просто не означает, что вы (спрашивающий) думаете: это символ длины c_char, а не типа c_char. Если c_char не является 1, который не является (F2008) C-совместимым.   -  person francescalus    schedule 20.12.2016
comment
Вы можете смешивать их, просто не перенося, но это не очень умно. Просто создайте интерфейс Fortran 2008 для старого кода, если вам не нужно поддерживать какую-то старую устаревшую систему.   -  person Vladimir F    schedule 20.12.2016


Ответы (2)


Я пытаюсь вызвать некоторый код Fortran77 из C, но я не нашел правильного способа передачи массива символов C.

Все реализации Fortran 77, которые я когда-либо использовал, предоставляли специфичные для реализации механизмы взаимодействия с C. Как правило, они предполагают, что сторона C знает соглашения об изменении имен и передаче аргументов, используемые компилятором Fortran, и использует их в интерфейсе. Соглашения системы GCC не так уж сложны в использовании.

Однако вас, похоже, интересует средство взаимодействия Fortran/C, представленное в Fortran 2003. Предположим, что ваш код на Fortran 77 также совместим с Fortran 2003 (или его можно заставить сделать так), должна быть возможность написать интероперабельную оболочку на Fortran 2003. Однако имейте в виду, что средства взаимодействия C не обеспечивают (напрямую) для взаимодействия переменных Фортрана или параметров подпрограммы типа character с длиной больше 1 или видом, отличным от c_char. С другой стороны, имейте в виду, что длина символьного объекта Fortran — это не то же самое, что размер(а) массива символов.

У вас есть несколько интероперабельных альтернатив для предоставления C-обращенного интерфейса, с помощью которого можно принимать массив C char. Возможно, самым ясным было бы принять массив Fortran предполагаемого размера character:

SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
    USE ISO_C_BINDING
    IMPLICIT NONE
    CHARACTER(kind=C_CHAR), dimension(*), intent(in) :: c_message
    ! ...
END

Наиболее вероятная альтернатива - принять указатель массива C напрямую:

SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
    USE ISO_C_BINDING
    IMPLICIT NONE
    type(C_PTR), value :: c_message
    ! ...
END

Последнее необходимо, если указатель массива на стороне C может быть нулевым. Оба требуют явной передачи длины, если нельзя полагаться на то, что массив завершается нулем.

В любом случае, если вы в конечном итоге хотите, чтобы переменная Fortran character имела длину больше 1 (в отличие от массива, имеющего размерность больше 1), то совместимые интерфейсы не могут обеспечить это напрямую — например, типы не входят в число тех, к которым применимы условия взаимодействия C. Если вы не можете полагаться на то, что тип символов по умолчанию будет c_char, вам нужно будет связать их с копированием (/ копированием), чтобы преобразовать символы между типами. С прежним интерфейсом должно быть очевидно, как вы можете скопировать массив в скаляр Фортрана character, имеющий длину больше 1. Для варианта с указателем может быть полезно использовать функцию преобразования примерно так:

subroutine C_string_ptr_to_F_string(C_string, F_string)
    use ISO_C_BINDING
    type(C_PTR), intent(in) :: C_string
    character(len=*), intent(out) :: F_string
    character(len=1, kind=C_CHAR), dimension(:), pointer :: p_chars
    integer :: i
    if (.not. C_associated(C_string)) then
      F_string = ' '
    else
      call C_F_pointer(C_string, p_chars, [huge(0)])
      do i = 1, len(F_string)
        if (p_chars(i) == C_NULL_CHAR) exit
        F_string(i:i) = p_chars(i)
      end do
      if (i <= len(F_string)) F_string(i:) = ' '
    end if
end subroutine

(На основе подпрограммы C_F_string_ptr C_interface_module из Fortran Wiki)

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

person John Bollinger    schedule 20.12.2016
comment
Что касается последнего абзаца, копия требуется только в том случае, если необходимо преобразование в добрую сторону. Указатель отложенной длины типа C_CHAR может быть связан с той же последовательностью хранения, которая занята строкой C, если это необходимо. Из краткого обзора видно, что вики-страница не использует символ отложенной длины, предположительно потому, что на момент написания отсутствовала поддержка gfortran. - person IanH; 21.12.2016
comment
@IanH, спасибо за наблюдение. Я исправил и расширил свои комментарии, чтобы обсудить ассоциирование вместо копирования, которое действительно следует предпочесть там, где это возможно. - person John Bollinger; 21.12.2016

  SUBROUTINE FCODE(STR, N)
  CHARACTER*(*) STR
  INTEGER N
  WRITE(*,*), STR, N
  END


  void foo(char *str)
  {
      int N = 10;
      printf("Calling fortran\n");
      fcode_(str,  &N, strlen(str)); 
  }

Вам нужно передать длину sgtring в качестве скрытого параметра. См. мою статью здесь http://www.malcolmmclean.site11.com/www/MpiTutorial/CandFortran77.html

person Malcolm McLean    schedule 20.12.2016
comment
Данный код предполагает, что компилятор Фортрана оформляет свои символы определенным образом (нижний регистр, завершающее подчеркивание). Эта конвенция далеко не вездесуща. Условные обозначения длины скрытых символов также могут различаться. - person IanH; 21.12.2016
comment
Они действительно различаются. Патч для изменения его с int на size_t в gfortran уже на столе. Я предполагаю ОГРОМНЫЙ приток невежественных аскеров, которые использовали такой код. - person Vladimir F; 21.12.2016