LoginSignup
3
0

More than 3 years have passed since last update.

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

Posted at

はじめに

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

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

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

関数名について

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

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

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

forran/sub_hello.f90
subroutine Hello() BIND(C)
  implicit none
  print *, 'Hello World'
end subroutine
main.nim
{.compile("fortran/sub_hello.f90", "-lgfortran").} # 後述
{.passL:"-lgfortran".} # 後述

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

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

BIND(C)を用いない場合

forran/sub_hello.f90
subroutine hello()
  implicit none
  print *, 'Hello World'
end subroutine
main.nim
{.compile("fortran/sub_hello.f90", "-lgfortran").} # 後述
{.passL:"-lgfortran".} # 後述

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

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

リンク方法

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

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

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

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

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

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

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

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

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

オブジェクトファイルを生成
$ gfortran -c -o fortran/sub_hello.o fortran/sub_hello.f90
main.nim
# fortran/sub_hello をリンク対象に追加する
{.link: "fortran/sub_hello.o".}
# リンク時のオプションに -lgfortran を追加する
{.passL: "-lgfortran".}

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

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

静的ライブラリをリンク

静的ライブラリを生成
$ gfortran -c -o fortran/sub_hello.o fortran/sub_hello.f90
$ ar rcs -o fortran/libsub_hello.a fortran/sub_hello.o
main.nim
# ./fortran/libsub_hello.a を静的リンク、コンパイル時に -lgfortran オプションを追加する
{.passL: "-L./fortran -lsub_hello -lgfortran".}

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

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

動的ライブラリをリンク

動的ライブラリを生成
$ gfortran -shared -fpic -o fortran/libsub_hello.so fortran/sub_hello.f90 
main.nim
# fortran/libsub_hello.so を動的リンク
proc call_hello() {.dynlib: "fortran/libsub_hello.so",importc: "hello".}

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

戻り値を受け取る

Fortranからinteger型の1を受け取る。

forran/sub_one.f90
function one() BIND(C)
  Use Iso_C_Binding
  implicit none
  integer :: one
  one = 1
end function
main/nim
{.compile("fortran/sub_one.f90", "-lgfortran").}
{.passL: "-lgfortran".}

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

var one:int32
one = call_one()
echo one
実行
$ nim c -r main.nim
 1

引数を与える

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

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

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
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
実行
$ nim c -r main.nim
 3

ptr型で渡す

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
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
実行
$ nim c -r main.nim
 3

ref型で渡す

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
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
実行
$ 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

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0