Передача массива C-строк в Fortran (iso_c_binding)

Как передать массив строк C (char* cstrings[]) в подпрограмму Fortran?

Вопрос Массивы строк в мостах fortran-C с использованием iso_c_binding определенно связан с , но ответ кажется неправильным и даже не компилируется с GNU Fortran.

В настоящее время я работаю над интерфейсом C для кода Fortran, и я ожидал, что iso_c_binding (который я использовал ранее) сделает это проще простого. Пока не повезло с массивами строк C ...

Подпрограмма Fortran должна принимать в качестве аргумента массив строк. На простом Фортране я бы написал примерно следующее:

subroutine print_fstring_array(fstring)

  implicit none

  character(len=*), dimension(:), intent(in) :: fstring
  integer                                    :: i

  do i = 1, size(fstring)
    write(*,*) trim(fstring(i))
  end do

end subroutine print_fstring_array

Один из способов передать одну строку C в Фортран - это указатель C (c_ptr) (я знаю, я мог бы также использовать массив character(kind=c_char))

subroutine print_cstring(cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_f_pointer, c_loc, c_null_char
  implicit none

  type(c_ptr), target, intent(in) :: cstring
  character(len=1024), pointer    :: fstring
  integer                         :: slen

  call c_f_pointer(c_loc(cstring), fstring)
  slen = index(fstring, c_null_char) - 1
  write(*,*) fstring(1:slen)

end subroutine print_cstring

Итак, я предположил, что массив c_ptr будет хорошей идеей

subroutine print_cstring_array(n, cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
  implicit none

  integer(kind=c_int),               intent(in) :: n
  type(c_ptr), dimension(n), target, intent(in) :: cstring
  character(len=1024), pointer                  :: fstr
  integer                                       :: slen, i

  do i = 1, n
    call c_f_pointer(c_loc(cstring(i)), fstring)
    slen = index(fstring, c_null_char) - 1
    write(*,*) fstring(1:slen)
  end do

end subroutine print_cstring_array

но это приводит к ошибке сегментации.

Код C для последнего примера:

# include "stdio.h"
# include "fstring.h"

void main(void) {
  char* cstring[] = { "abc", "def", "ghi", "jkl" };
  int n = 4;
  print_cstring_array(&n, cstring);
}

а содержимое файла заголовка fstring.h просто:

void print_cstring_array(int* n, char* cstring[]);

Я нацелен на GNU Fortran и Intel Fortran и протестировал вышеперечисленное с GNU Fortran. Длина строк фиксирована (3 в приведенном выше примере), на случай, если это упростит решение. Однако размер массива может быть разным.

Приветствуются любые указатели (даже указатели C).


person alexurba    schedule 01.09.2014    source источник
comment
Обратите внимание: char* cstrings[] - это массив указателя на строки, он не обязательно относится к непрерывному блоку памяти. Посмотрите здесь: stackoverflow.com/q/13869528/694576 и обратите внимание на разницу в том, как определяется массив строк.   -  person alk    schedule 01.09.2014
comment
Спасибо @alk. Это может быть проблемой здесь. Мои навыки Си не очень хороши. Как мне сделать это непрерывным?   -  person alexurba    schedule 01.09.2014
comment
Как мне сделать это непрерывным? Делая это так, как это делает код в вопросе, который я дал.   -  person alk    schedule 01.09.2014
comment
Ключ Google для решения таких проблем - Mixed Language Programming.   -  person alk    schedule 01.09.2014
comment
Спасибо @alk, этой ссылки не было в вашем первоначальном комментарии. Действительно, этот ответ выглядит многообещающим. Теперь я пытаюсь разобраться в деталях.   -  person alexurba    schedule 01.09.2014
comment
Также @alk, поверьте, я часами искал решение в Google. На форуме много сообщений, которые на первый взгляд казались связанными, но оказались бесполезными. Фактически, это первый раз, когда я прибегаю к вопросу о SO.   -  person alexurba    schedule 01.09.2014
comment
Удачи. Просто так давно я сделал именно это. Резервная копия кода находится в резервной копии резервной копии ... извините.   -  person alk    schedule 01.09.2014
comment
Какую часть вы хотите сохранить (более или менее), ваш код C здесь или вашу подпрограмму Fortran?   -  person Vladimir F    schedule 01.09.2014
comment
@alk - я решил. Я просто не знал, что c_f_pointer(cptr, fptr[, shape]) принимает форму массива в качестве необязательного третьего аргумента (найденного в ответе, на который вы указали). Вы можете опубликовать это в качестве ответа. В противном случае я сделаю это сам, так как считаю, что это полезно для других людей и не идентично связанному вопросу.   -  person alexurba    schedule 01.09.2014
comment
@Vladimir - Я пишу часть Fortran, но часть C должна быть максимально простой для пользователей. В любом случае, благодаря alk я решил проблему.   -  person alexurba    schedule 01.09.2014


Ответы (2)


Самая большая проблема в вашем коде в вопросе - вы используете c_f_pointer(c_loc(cstring), вместо c_f_pointer(cstring,.

Это работает для меня:

subroutine print_cstring_array(n, cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
  implicit none

  integer(kind=c_int),               intent(in) :: n
  type(c_ptr), dimension(n), target, intent(in) :: cstring
  character, pointer                            :: fstring(:)
  integer                                       :: slen, i

  do i = 1, n
    call c_f_pointer(cstring(i), fstring, [4])
    write(*,*) fstring
  end do

end subroutine print_cstring_array



# include "stdio.h"

void print_cstring_array(int* n, char* cstring[]);

void main(void) {
  char* cstring[] = { "abc", "def", "ghi", "jkl" };
  int n = 4;
  print_cstring_array(&n, cstring);
}
person Vladimir F    schedule 01.09.2014
comment
Спасибо за ответ. Однако я думаю, что это чистая удача (или зависит от компилятора), что вызов c_f_pointer работает в вашем примере. Вам определенно нужна ячейка памяти C из c_loc. См., Например, _ 3_ (Intel Fortran). - person alexurba; 01.09.2014
comment
@alexurba Нет, нормально. Я передаю type(C_PTR) c_f_pointer, который принадлежит к правильному типу. Ваш отзыв это подтверждает, хотя я знаю c_f_pointer достаточно хорошо. - person Vladimir F; 01.09.2014
comment
Хорошо, теперь я понял. Спасибо за разъяснение @Vladimir! - person alexurba; 01.09.2014
comment
@VladimirF - я пытаюсь передать 1-d char *: char *cstring = {"hello world"};, (пропуская dimension(n) в объявлении cstring, а также цикл do) и просто использую call c_f_pointer(cstring, fstring, [lengthofcstring]), но все, что я получаю в fstring, - это указатели undefined на lengthofcstring (= 11). Почему ваш последний параметр в c_f_pointer = 4? Разве не должно быть 3? - person Erik Thysell; 03.02.2016
comment
Да, 3, но 4 тоже работает, четвертый нулевой символ добавляется буквой C и не печатается. - person Vladimir F; 03.02.2016

Иногда решение оказывается проще, чем ожидалось. Оказалось, что c_f_pointer(cptr, fptr[, shape]) принимает форму массива как необязательный аргумент для преобразования массивов указателей C (я пропустил это в справочнике):

subroutine print_cstring_array(n, cstring) bind(C)

  use iso_c_binding, only: c_ptr, c_int, c_f_pointer, c_loc, c_null_char
  implicit none

  integer(kind=c_int),                 intent(in) :: n
  type(c_ptr), target,                 intent(in) :: cstring
  character(kind=c_char), dimension(:,:), pointer :: fptr
  character(len=3), dimension(n)                  :: fstring

  call c_f_pointer(c_loc(cstring), fptr, [3, n])
  do i = 1, n
     slen = 0
     do while(fptr(slen+1,i) /= c_null_char)
        slen = slen + 1
     end do
     fstring(i) = transfer(fptr(1:slen,i), fstring(i))
     write(*,*) trim(fstring(i))
  end do                                                

end subroutine print_cstring_array

Спасибо @alk за то, что указал мне на Как передавать массивы строк из C и Fortran в Fortran?, потому что там я реализовал необязательный аргумент shape для c_f_pointer(cptr, fptr[, shape]).

person alexurba    schedule 01.09.2014
comment
Это выглядит слишком сложно, что такое atom_types? Вы этого нигде не декларируете. - person Vladimir F; 01.09.2014
comment
Вызывая c_loc, вы фактически создаете указатель на указатель. Ваш подход потерпит неудачу, если ваш массив строк не будет непрерывным, то есть когда отдельные строки будут динамически распределяться. - person Vladimir F; 01.09.2014
comment
Извините за atom_types - это была явно опечатка. Теперь я понимаю, что мне не требуется c_loc. Ваше решение правильное. Спасибо, @Vladimir. - person alexurba; 01.09.2014