Fortranのdeferred length characterのバージョン依存性

Fortran2003から導入されたdeferred length character、つまり可変長文字列ですが、gccのバージョンでちょっと挙動が変わっていたので、まとめてみます。

# 比較したバージョン

* GCC 4.8.4
  - AtCoderのコードテスト
* GCC 4.8.5
  - CentOS 7
* GCC 5.4.0
  - Ubuntu 16.04
 
# 基本

```fortran
program main
   implicit none
   integer :: W
   character(len=:), allocatable :: S
   W = 10
   allocate(character(len=W) :: S)
   write(*, *) S
   write(*, *) "string len: ", len(S)
   S = "hoge"
   write(*, *) S
   write(*, *) "string len: ", len(S)
end program main
```
これを実行すると以下の出力を得ます。

```
 X�؉�X�
 string len:           10
 hoge
 string len:            4
```

可変長文字列なのでメモリの割当が必要となります。allocateを呼び出す場合は

```fortran
allocate(character(len=W) :: S)
```
とします。ですがわざわざ`allocate`しなくても`S = "hoge"` とすると必要な分だけallocateされます。

# 可変長文字列の可変長配列

## 入力

AtCoderでよくある、1行目に$H$と$W$、続く$H$行で長さ$W$の文字列が入力される状況を考えます。

```
3 4
abcd
efgh
ijkl
```

## GCC5.4.0
5.4.0では以下のようにして文字列を取得できます。

```fortran
program main
   implicit none
   integer :: W, H, y
   character(len=:), allocatable :: S(:)
   read(*, *) H, W
   allocate(character(len=W) :: S(H))
   read(*, *) S
   do y = 1, H
   write(*, *) S(y)
   enddo
end program main
```

長さ`W`の文字列の`H`個の配列を `allocate(character(len=W) :: S(H))`としておけば、
`read(*, *) S`の一回で`H`行の文字列をすべて読み込めます。

ちなみに2行目3文字目のみを取り出すには

```fortran
   write(*, *) S(2)(3:3)
```
とします。

## GCC 4.8では・・・

次のように、ちゃんと配列を割り当てられたかどうかをチェックするコードを挟んで見ます。

```fortran
program main
   implicit none
   integer :: W, H, y
   character(len=:), allocatable :: S(:)
   write(*, *) "Input height and width"
   read(*, *) H, W
   allocate(character(len=W) :: S(H))
   write(*, *) "Height (array size): ", size(S)
   write(*, *) "Width (string len): ", len(S(1))
   read(*, *) S
   do y = 1, H
       write(*, *) S(y)
   enddo
end program main
```

これを実行して、上で紹介した入力を与えてやると、

* 4.8.4

```
 Input height and width
 Height (array size):            3
 Width (string len):            0



```

* 4.8.5

```
 Input height and width
 Height (array size):            3
 Width (string len):      4196752
abcd (めちゃ長い空白)
以下略
```

全然ダメです。特に4.8.4はデータをもたせることすらできません。

# 回避策

## Type Array（失敗）

インテルコンパイラーに妙に詳しい人が、Typeの中にDeferred length characterをメンバにもたせ、そのTypeの動的Arrayを使うと良いと提案していました。

https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/279148

```fortran
   type str
      character(len=:), allocatable :: t
   end type str
   type(str), allocatable :: S(:)
```

これをGCC4.8.5でコンパイルすると、

```
type_array.f08:6.40:

      character(len=:), allocatable :: t
                                        1
エラー: Deferred-length character component 't' at (1) is not yet supported
```
とのことです。使えなかった。それにこの方法は

```
S = "hogehoge"
```
といった代入もできないので、できたらやりたくないところ。

## 固定長文字列配列

もうdeferred length characterは諦め、従来通りの長めに取った固定長文字列を使います。

```fortran
program main
   implicit none
   integer :: W, H, y
   character(len=100), allocatable :: S(:)
   write(*, *) "Input height and width"
   read(*, *) H, W
   allocate(S(H))
   write(*, *) "Height (array size): ", size(S)
   write(*, *) "Width (string len): ", len(S(1))
   read(*, *) S
   do y = 1, H
      write(*, *) trim(S(y))
   enddo
end program main        
```

これを上の入力で実行すると、出力は

```
 Input height and width
 Height (array size):            3
 Width (string len):          100
 abcd
 efgh
 ijkl
```
と、固定長の長さが確認できます。
また、`read(*, *) S`の1回で、入力の各行が`S`の各成分に代入されました。
`S(1)`にすべて押し込まれるといったことがなくて安心。

# ちなみに

可変長文字列の可変長配列ですが、代入してしまう場合の注意です。

```fortran
   character(len=:), allocatable :: S(:)
   S = ["hoge", "fugafuga"]
```
これはエラーです。

```
    S = ["hoge", "fugafuga"]
               1
Error: Different CHARACTER lengths (4/8) in array constructor at (1)
```

以下のようにスペースを入れて水増しします。

```fortran
   S = ["hoge    ", "fugafuga"]
   write(*, *) S
   write(*, *) "Height (array size): ", size(S)
   write(*, *) "Width (string len): ", len(S(1))
```
これの出力は以下のようになります。

```
 hoge    fugafuga
 Height (array size):            2
 Width (string len):            8
```

# おわりに

Fortranの文字列操作は、Cに比べるとかなりやりやすい方ですが、現代的な言語に比べるとやはり見劣りしますね。