1

私は手元に次のプログラムを持っています

program foo
  type bar
    real, dimension(2) :: vector
  end type
  type(bar), dimension(3) :: bararray
  call doSomething(bararray%vector)
end program

subroutine doSomething(v)
  real, dimension(3,2), intent(inout) :: v
  ...
end subroutine

これでコンパイルエラーが発生します。

Error: Two or more part references with nonzero rank must not be specified at (1)

呼び出しを変更すると

call doSomething((/bararray%vector(1), bararray%vector(2)/))

すべてがうまくいきます。問題は、これは少し面倒に思えるだけなので、問題は、サブルーチンの引数を記述する他の方法はないかということです。

前もって感謝します。

4

2 に答える 2

6

エラーが発生するのは、構造コンポーネント (または同様のマルチパート参照) の 1 つの部分のみがゼロ以外のランクを持つことができるという制約 (F2008 では構文規則は C618) があるためです。構造コンポーネントへの参照bararray%vectorには、ランクがゼロではないコンポーネントvectorと変数の 2 つの部分がありますbararray

(「呼び出しを変更する」アプローチでは、コンポーネント ベクトルへの参照には、その部分に全体的なランク 0 を与える添え字があるため、bararray%vector(1) が受け入れられます。)

「コールを変更する」アプローチには重大な潜在的な問題があります。

サブルーチンの仮引数は ですINTENT(INOUT)。これには、実際の引数が定義可能である必要があります (実際に「可変」できる変数)。

「呼び出しを変更する」アプローチでは、その仮引数に関連付けられている実際の引数は式、つまり配列コンストラクターです。式は定義できません。式を評価した結果は値であり、「定義」できるものではありません2 + 2 = 6

おそらく実際のコードでは、doSomething サブルーチンは外部プロシージャであるため、コンパイラはこれを診断していません。doSomething に明示的なインターフェースがある場合 (おそらくモジュール内にあったため)、コンパイラーがエラーを報告することを期待します。

他の人は、呼び出しの前にデータを再マーシャリングするアプローチを提案しています (適切なサイズの配列変数にデータをコピーし、プロシージャを呼び出し、データをコピーします)。bar 型のオブジェクトを受け取るようにサブルーチンのインターフェースを書き直す (型の定義をモジュールに移動する) ことは、明らかに別の可能性です。派生型は、データを格納する便利な方法であるだけでなく、多くの場合、データを操作する便利な方法です。

プロセッサをだまして、メモリ内の仮想配列のレイアウトであると「知っている」ものを受け入れさせようとするアプローチには非常に警戒しbararray%vectorます。型と配列のレイアウトはプロセッサごとに変わる可能性があり、さらにプロセッサのエラー チェックが改善されると、その種のトリックが後の診断につながる可能性があります。ベンダーが提供するライブラリは、その種のトリックを回避できますが、単なるプログラミング人間ではありません。

于 2012-08-15T21:19:19.140 に答える
5

Intel Fortran (13.0.xxx) コンパイラでエラーが発生する

error #6159: A component cannot be an array if the encompassing structure is an array.

あなたの質問に対する答えは、引数をとるサブルーチンを呼び出す適切な方法は、サブルーチンを呼び出す前に何らかの方法でデータをマーシャリングする必要がreal, dimension(3,2)ある引数を与えることだと思います。real, dimension(3,2)はい、これは少し面倒ですが、厳密な型チェックはあると便利な機能ではありませんか?

次の行に沿ってラッパー サブルーチンを定義できます。

subroutine wrapDoSomething(abar)
  type(bar), dimension(:), intent(inout) :: abar
    ...
    ... marshal your data ...
    call subroutine doSomething(marshalled_data)
    ...
end subroutine wrapDoSomething

明示的なインターフェイスを定義せずに定義すると、呼び出し元が s の配列であると考えるものの、サブルーチンがランク 2 の s の配列として取得するsubroutine doSomethingものを渡してもうまくいくのではないかと思います。それがブロックからすぐに機能しない場合は、型定義で使用すると、コンパイラが6つの実数に連続したストレージを割り当ててしまう可能性があります。barrealBIND(C)

もしすべてがあなたの目の前で爆発したら、あなたは独りで、その任務への私の関与は否定されます。

于 2012-08-15T10:39:17.107 に答える