NimからFortranを呼び出す（ビルド方法、整数型の利用）

# はじめに
NimからのFortranの関数の呼び出すためのビルド方法と整数型の利用方法を示す。
注：Fortranでは戻り値がない関数はサブルーチンと呼ぶが本記事では全て関数と記載する。

NimのデフォルトのバックエンドはC言語なので、C言語からFortranを呼び出し方法が参考となる。

本記事ではOSはLinux、コンパイラはgccおよびgfortranを用いる。

# 関数名について
NimでC言語の関数を呼び出す場合には`importc`プラグマで呼び出す関数名を指定できる。
Fortranの関数も同様に指定して呼び出せるが、次の2点に気を付ける必要がある。

- Fortarnの関数名に大文字が含まれている場合でもNimでの指定はすべて小文字とする
- Fortran側で`BIND(C)`を用いるか、Nim側の呼び出し名の最後に`_`を追加する

### Fortranの関数名に大文字が含まれている場合

```fortran:forran/sub_hello.f90
subroutine Hello() BIND(C)
  implicit none
  print *, 'Hello World'
end subroutine
```

```nim:main.nim
{.compile("fortran/sub_hello.f90", "-lgfortran").} # 後述
{.passL:"-lgfortran".} # 後述

proc call_hello() {.importc: "hello".} # Helloではなくhelloと指定する

call_hello()
```

```bash:実行
$ nim c -r main.nim
 Hello World
```

### `BIND(C)`を用いない場合

```fortran:forran/sub_hello.f90
subroutine hello()
  implicit none
  print *, 'Hello World'
end subroutine
```

```nim:main.nim
{.compile("fortran/sub_hello.f90", "-lgfortran").} # 後述
{.passL:"-lgfortran".} # 後述

proc call_hello() {.importc: "hello_".} # helloではなくhello_と指定する

call_hello()
```

```bash:実行
$ nim c -r main.nim
 Hello World
```

# リンク方法
Fortranとのリンク方法について4つの方法に示す。

- ソースファイルを一緒にビルド
- オブジェクトファイルを一緒にビルド
- 静的ライブラリをリンク
- 動的ライブラリをリンク

各方法に用いるFortranソースは共通とする。

```fortran:forran/sub_hello.f90
subroutine hello() BIND(C)
  implicit none
  print *, 'Hello World'
end subroutine
```

### ソースファイルを一緒にビルド

```nim:main.nim
 # ビルド対象に fortran/sub_hello.f90 を加え、コンパイル時に -lgfortran オプションを追加する
{.compile("fortran/sub_hello.f90", "-lgfortran").}
# リンク時のオプションに -lgfortran を追加する
{.passL: "-lgfortran".}

proc call_hello() {.importc: "hello".}

call_hello()
```
```bash:実行
$ nim c -r main.nim
 Hello World
```

### オブジェクトファイルを一緒にビルド

```bash:オブジェクトファイルを生成
$ gfortran -c -o fortran/sub_hello.o fortran/sub_hello.f90
```

```nim:main.nim
# fortran/sub_hello をリンク対象に追加する
{.link: "fortran/sub_hello.o".}
# リンク時のオプションに -lgfortran を追加する
{.passL: "-lgfortran".}

proc call_hello() {.importc: "hello".}

call_hello()
```

```bash:実行
$ nim c -r main.nim
 Hello World
```

### 静的ライブラリをリンク

```bash:静的ライブラリを生成
$ gfortran -c -o fortran/sub_hello.o fortran/sub_hello.f90
$ ar rcs -o fortran/libsub_hello.a fortran/sub_hello.o
```

```nim:main.nim
# ./fortran/libsub_hello.a を静的リンク、コンパイル時に -lgfortran オプションを追加する
{.passL: "-L./fortran -lsub_hello -lgfortran".}

proc call_hello() {.importc: "hello".}

call_hello()
```

```bash:実行
$ nim c -r main.nim
 Hello World
```

### 動的ライブラリをリンク

```bash:動的ライブラリを生成
$ gfortran -shared -fpic -o fortran/libsub_hello.so fortran/sub_hello.f90 
```

```nim:main.nim
# fortran/libsub_hello.so を動的リンク
proc call_hello() {.dynlib: "fortran/libsub_hello.so",importc: "hello".}

call_hello()
```

```bash:実行
$ nim c -r main.nim
 Hello World
```

# 戻り値を受け取る
Fortranからinteger型の`1`を受け取る。

```fortran:forran/sub_one.f90
function one() BIND(C)
  Use Iso_C_Binding
  implicit none
  integer :: one
  one = 1
end function
```

```nim:main/nim
{.compile("fortran/sub_one.f90", "-lgfortran").}
{.passL: "-lgfortran".}

proc call_one(): int32 {.importc: "one".}

var one:int32
one = call_one()
echo one
```

```bash:実行
$ nim c -r main.nim
 1
```

# 引数を与える
Fortran関数の引数は基本的に参照渡しのため、以下のどれかが必要となる。

- Fortranの関数を値渡しで定義する
- `ptr`型で渡す
- `ref`型で渡す

### Fortranの関数を値渡しで定義する

```fortran:forran/sub_add.f90
function add(a,b) BIND(C)
  Use Iso_C_Binding
  implicit none
  integer(4), value :: a, b ! VALUE属性を付与して値渡しにする
  integer(4) :: add
  add = a + b
end function
```

```nim:main/nim
{.compile("fortran/sub_add.f90", "-lgfortran").}
{.passL: "-lgfortran".}

proc call_add(a, b: int32): int32 {.importc: "add".} 

var sum = call_add(1, 2)
echo sum
```

```bash:実行
$ nim c -r main.nim
 3
```

### `ptr`型で渡す

```fortran:forran/sub_add.f90
function add(a,b) BIND(C)
  Use Iso_C_Binding
  implicit none
  integer(4) :: a, b
  integer(4) :: add
  add = a + b
end function
```

```nim:main/nim
{.compile("fortran/sub_add.f90", "-lgfortran").}
{.passL: "-lgfortran".}

proc call_add(a, b: ptr int32): int32 {.importc: "add".} 

var a = 1.int32
var b = 2.int32

var sum = call_add(a.addr, b.addr)
echo sum
```

```bash:実行
$ nim c -r main.nim
 3
```

### `ref`型で渡す

```fortran:forran/sub_add.f90
function add(a,b) BIND(C)
  Use Iso_C_Binding
  implicit none
  integer(4) :: a, b
  integer(4) :: add
  add = a + b
end function
```

```nim:main/nim
{.compile("fortran/sub_add.f90", "-lgfortran").}
{.passL: "-lgfortran".}

proc call_add(a, b: ref int32): int32 {.importc: "add".} 

var a = new(int32)
var b = new(int32)
a[] = 1
b[] = 2

var sum = call_add(a, b)
echo sum
```

```bash:実行
$ nim c -r main.nim
 3
```

# 参考
https://docs.oracle.com/cd/E19957-01/806-4843/Cp11_cfort.html
https://www.nag-j.co.jp/fortran/tips/tips_InteroperabilityWithC.html