5

文字列の配列を C から Fortran サブルーチンに、また Fortran から同じ Fortran サブルーチンに渡そうとしています。C と Fortran の両方から、単一の文字列 (つまり、1D 文字配列) を正常に渡すことができました。ただし、文字列の配列に問題があります。私は Fortran 側で ISO C バインディングを使用しています。理想的には、これを呼び出し側で可能な限りシームレスにしたいと考えています。

関連する質問と回答をいくつか読みました。いくつか (つまり、これこれ) は、詳細がなく、単に「ISO C を使用する」だけであり、あまり役に立ちません。この回答は非常に役に立ちました (別の質問に対する同様の回答) が、c_null_char が単一の Fortran 文字列で認識されるように見える単一の文字列に対してのみ機能します。2 つの別個のルーチンがなければ、配列の場合に何をすべきかわかりません。

私が現在持っているのは、文字列の配列 ( string)を渡したい C ルーチンです。

#include <iostream>

extern "C" void print_hi_array(char input_string[][255]);

using namespace std;

int main() {

  char string[3][255] = {"asdf","ghji","zxcv"};   
  print_hi_array(string);

  return 0;
}

そして、同様の Fortran ルーチン:

program main
  implicit none
  call print_hi_array( (/"asdf", "ghji", "zxcv"/) )
end program

これまでのところ、これは私が受信側のために持っているものです:

subroutine print_hi_array(input_string) bind(C)
  use iso_c_binding, only: C_CHAR, c_null_char

  implicit none

  character (kind=c_char, len=1), dimension (3,255), intent (in) :: input_string
  character (len=255), dimension (3) :: regular_string
  character (len=255) :: dummy_string
  integer :: i,j,k

  write (*,*) input_string

  do j = 1 , 3
    dummy_string(:) = c_null_char
    k = 1
    do i = 1 + (j-1)*255, j*255,1
      if (input_string(i) .ne.  c_null_char) then
        write (*,*) "i ",i,j, input_string(i)
        dummy_string(k:k) = input_string(i)
      endif
    k = k +1
    enddo
    regular_string(j) = dummy_string
  enddo

  write (*,*) regular_string

end subroutine print_hi_array

これは C 関数で機能します。私はこの出力を得ます:

 asdfghjizxcv
 j=           1
 i            1           1 a
 i            2           1 s
 i            3           1 d
 i            4           1 f
 j=           2
 i          256           2 g
 i          257           2 h
 i          258           2 j
 i          259           2 i
 j=           3
 i          511           3 z
 i          512           3 x
 i          513           3 c
 i          514           3 v
 asdf   ghji   zxcv   

ただし、Fortran を使用して行うと、ナンセンスになります。

asdfghjizxcv@O,B�@(P,B�]B]6(P,B�@ .......

c_null_charこのアプローチにはないようです。

では、C と Fortran の両方から文字列の配列を取り込む Fortran サブルーチンを作成するにはどうすればよいでしょうか?

4

3 に答える 3

5

Fortran は、格納されたテキストよりも長い文字列が宣言されている場合、スペースを使用して残りの文字列を埋めます。ゼロ区切りではなく、宣言された長さが隠し変数に格納されます。c null char が含まれていないため、ガベージを読み取っています (バッファ オーバーフロー)。tlit が \000 を含む文字列を出力するときに Fortran が出力する内容は、標準では定義されておらず、実装に依存します。

特に、次元 3 の character(4) 配列を、より多くのデータ (255 文字、インデックスの順序についてはわかりません) を期待するサブルーチンに渡しています。ポインタしか渡されないのでチェックできないと思います。

この方法で、配列コンストラクターで文字列の長さを定義することができます。

[character(255) :: "a","ab","abc"]
于 2012-12-13T22:43:34.893 に答える
3

実際には2つの方法があります。いずれかの方法で、C でループを作成し、以前に行ったように、文字列を 1 つずつ Fortran に渡します。あるいは、配列全体を渡し、 Fortran と C の配列を同じルーチンで処理たい場合は、C 文字列配列の適切なコピーを作成する必要があります。動作するがあまりテストされていない例の下:

extern "C" void print_array_c(int nstring, char input_string[][255]);

using namespace std;

int main() {

  char string[3][255] = {"asdf","ghji","zxcv"};    
  print_array_c(3, string);

  return 0;
}

この例ではさまざまなサイズの配列を処理できるように、文字列の数も渡すことに注意してください。(ただし、文字列の長さは 255 文字と想定されています。) Fortran のサイズでは、Fortran 文字列を変換するルーチンが必要になります。考えられる視覚化の 1 つとして、次のようなものがあります。

module arrayprint_module
  use, intrinsic :: iso_c_binding
  implicit none

  integer, parameter :: STRLEN = 255

contains

  !> The printing routine, works with Fortran character arrays only.
  subroutine print_array(strings)
    character(len=STRLEN), intent(in) :: strings(:)

    integer :: ii

    do ii = 1, size(strings)
      write(*,*) ii, strings(ii)
    end do

  end subroutine print_array


  !> Converts C string array to Fortran string array and invokes print_array.
  subroutine print_array_c(nstring, cptr) bind(C)
    integer(c_int), value :: nstring
    type(c_ptr), intent(in), value :: cptr

    character(kind=c_char), pointer :: fptr(:,:)
    character(STRLEN), allocatable :: fstrings(:)
    integer :: ii, lenstr

    call c_f_pointer(cptr, fptr, [ STRLEN, nstring ])
    allocate(fstrings(nstring))
    do ii = 1, nstring
      lenstr = cstrlen(fptr(:,ii))
      fstrings(ii) = transfer(fptr(1:lenstr,ii), fstrings(ii))
    end do
    call print_array(fstrings)

  end subroutine print_array_c


  !> Calculates the length of a C string.
  function cstrlen(carray) result(res)
    character(kind=c_char), intent(in) :: carray(:)
    integer :: res

    integer :: ii

    do ii = 1, size(carray)
      if (carray(ii) == c_null_char) then
        res = ii - 1
        return
      end if
    end do
    res = ii

  end function cstrlen


end module arrayprint_module

これが機能するには、C から渡す配列が連続している必要があることに注意してください。また、文字 (kind=c_char) が fortran 文字型と互換性があると仮定しましたが、通常はそうあるべきです。

于 2012-12-14T14:28:18.870 に答える