5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Fortranのsubmoduleの使い方:もう依存関係に悩まされない

5
Last updated at Posted at 2024-01-05

Fortran2008で導入されたsubmoduleが便利でしたので紹介します。こちらのsubmodule でインターフェース分離の記事を参考にさせていただきました。

依存関係あるある

Fortranで複数ファイルをmakeすることを考えます。例えば、二つのファイル

mod1.f90
module mymodules1
    implicit none
    contains

    subroutine sub1()
        use mymodules2,only:print2
        write(*,*) "in sub1"
        call print2()
    end
end 
mod2.f90
module mymodules2 
    implicit none
    contains

    subroutine sub2()
        use mymodules1,only:sub1
        write(*,*) "in sub2"
        call sub1()        
    end

    subroutine print2()
        write(*,*) "in sub2, print"
    end
end 

という二つのファイルがあるとします。このファイル、普通のやり方だとコンパイルが通りません。なぜなら、mymodule1はmymodule2のサブルーチンを呼び、mymodule2はmymodule1のサブルーチンを呼んでいるため、どちらを先にコンパイルしてもエラーが出てしまうからです。つまり、依存関係がウロボロスの輪になっていて、どちらからもコンパイルができないのです。こうならないようにするには、どのmoduleにどのサブルーチンを属すべきかを慎重に考える必要がありますよね。

しかし、submoduleを使えば、そのようなことをせずにほとんどそのままコンパイルできます。

submodule

submoduleを使って書き換えてみます。まず、先ほどの二つのファイルを、moduleではなくsubmoduleにします。つまり、

mod1.f90
submodule(mymodules1) mod1
    implicit none
    contains

    subroutine sub1()
        use mymodules2,only:print2
        write(*,*) "in sub1"
        call print2()
    end
end submodule

と

mod2.f90
submodule(mymodules2) mod2
    implicit none
    contains

    subroutine sub2()
        use mymodules1,only:sub1
        write(*,*) "in sub2"
        call sub1()        
    end

    subroutine print2()
        write(*,*) "in sub2, print"
    end
end submodule

とします。やったことは、moduleをsubmoduleにしただけです。ただし、submoduleというからには親となるmoduleがいりまして、今回は

mymodules1.f90
module mymodules1
    implicit none
    interface 
        module subroutine sub1()
        end
    end interface
end module

と

mymodules2.f90
module mymodules2
    implicit none
    interface 
        module subroutine sub2()
        end
        module subroutine print2()
        end
    end interface
end module

とします。どのようなサブルーチンや関数が入っているかをこれらのmoduleのinterfaceブロック内に記述してあります。実は、これで、この二つのファイルを先にコンパイルすれば、先ほどのウロボロスの輪は解消されます。

makefileは

makefile
FCMP =  gfortran

PROG       = a.out

moduledir = ./modules
SRCmodule = $(wildcard $(moduledir)/*.f90)
OBJmodule = $(SRCmodule:.f90=.o)

maindir = ./main
SRCmain = $(wildcard $(maindir)/*.f90)
OBJmain = $(SRCmain:.f90=.o)

OBJ = \
        $(OBJmodule) \
		$(OBJmain) \
		main.o

.SUFFIXES:
.SUFFIXES: .o .f90   $(SUFFIXES)


%.o: %.f90
	$(FCMP) -c  $< -o $@ 	

$(PROG): $(OBJ)
	$(FCMP)  -o $(PROG) $(OBJ)

clean:
	rm -f *.o  *.*mod  *~ 
	rm -f $(PROG)	
	rm -f $(maindir)/*.o $(maindir)/*.*mod $(maindir)/*~ 
	rm -f $(moduledir)/*.o $(moduledir)/*.*mod $(moduledir)/*~ 

とします。これは、modulesディレクトリとmainディレクトリ内に入っている.f90ファイルをコンパイルするようにしています。modulesディレクトリ内に、mymodules1.f90とmymodules2.f90があり、こちらを先にコンパイルするようにしています。

このように、submoduleを使うことでinterfaceを先にコンパイルしてしまうことで、実装部分でどのような順番でコンパイルするかということに悩むこともなく、お互いを参照しているようなmoduleたちもコンパイルすることができるようになります。

なお、これがどのような時に役に立つかといいますと、FORTRANのModernizeに使えるいくつかの機能に紹介されている「異様に多いソースファイル群」を改良するときです。これは一昔前によく行われていた「ファイルに1 procedureだけを書くというスタイル」のコードたちをmoduleにまとめようとした時に、元のコードがmoduleを使わずに関数たちが定義されている場合に問題になることがあります。それぞれの関数が互いに参照されている場合、機能ごとにmoduleに分けようとすると、これまでは小さなファイルを連結して大きな一つのファイルにして一つのmoduleとしないとコンパイルを通すことができませんでした。submoduleを使えば、一つ一つの小さなファイルをそのままに、最初と最後をsubmoduleでラップすることで、機能ごとに違うmoduleとしてまとめることができ、その結果ファイル群を整理することができます。

5
8
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
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?