概要
C言語の__FILE__
, __LINE__
マクロは非常に有用な情報を提供してくれます.Fortranでもプリプロセスを有効化するコンパイルオプションを付与すれば,これらのマクロを利用できます.
使用環境
コンパイラ | バージョン |
---|---|
Intel Parallel Studio XE Composer Edition for Fortran | 19.0.0.117 |
PGI Visual Fortran for Windows | 18.7 |
GNU Fortran | 8.1.0 (MinGW-W64) |
定義済みマクロ
C/C++言語には,いくつかの定義済みマクロが用意されています.その中でも,__FILE__
マクロと__LINE__
マクロは,それが参照されたソースファイルの名前や行番号に展開されるので,プログラムの不整合,例外などの検出に利用できます.
__FILE__
は文字列,__LINE__
は整数に展開されます.簡単なプログラムを例示します.
#include <stdio.h>
int main(void)
{
printf("An error occurred in %s on line %d\n", __FILE__, __LINE__);
return 0;
}
実行すると,マクロを参照しているファイル名と行番号が正しく得られていることが確認できます.
An error occurred in main.c on line 5
FortranでのCマクロの参照
FortranでC言語のマクロを参照する場合,マクロ名はそのまま利用できます.加えて,コンパイル時にプリプロセスが行われるよう,コンパイルオプションを付与します.拡張子によっては自動でプリプロセスを行ってくれるようですが,そのために拡張子を変更するのは好ましくありません.可能な限りオプションを利用しましょう.
program main
implicit none
print '(A,I0)', "An error occurred in "//__FILE__//" on line ",__LINE__
end program main
Intel, PGI, GNUのFortranコンパイラにおけるオプションを示します.
コンパイラ | オプション |
---|---|
Intel | /fpp |
PGI |
-Mpreprocess もしくは-Mcpp
|
GNU Fortran | -cpp |
ifort /fpp main.f90
pgfortran -Mpreprocess main.f90
gfortran -cpp -ffree-line-length-none main.f90
An error occurred in main.f90 on line 3
Intel Fortranのコンパイルオプションは,Windowsの流儀に合わせて/
で始まっていますが,-
を用いても問題ありません.PGI Fortranには,-Mpreprocess
と-Mcpp
の二つのオプションが用意されていますが,-Mcpp
は入力ファイルをプリプロセスするだけのようです.
1行あたりの文字数制限はプリプロセス後のソースファイルに対しても課されるので,gfortranはプリプロセスを行う際に-ffree-line-length-none
オプションも併せて使うことを推奨しています1.
-E
オプションを付与すると,コンパイルは行われず,プリプロセスした結果が標準出力に表示されます.これはすべてのコンパイラで共通です.
> ifort /fpp -E main.f90 /nologo
# 1 "main.f90"
program main
implicit none
print '(A,I0)', "An error occurred in "//"main.f90"//" on line ",3
end program main
/nologo
は,コンパイラのコピーライト表示を抑制するオプションです.
> pgfortran -Mpreprocess -E main.f90
# 1 "main.f90"
program main
implicit none
print '(A,I0)', "An error occurred in "//"main.f90"//" on line ",3
end program main
> gfortran -cpp -E -ffree-line-length-none main.f90
# 1 "main.f90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.f90"
program main
implicit none
print '(A,I0)', "An error occurred in "//"main.f90"//" on line ",3
end program main
__LINE__の文字化
C言語での使われ方
__FILE__
, __LINE__
マクロは非常に便利なのですが,あちこちでエラーメッセージprint '(A,I0)', "An error occurred in "//__FILE__//" on line ",__LINE__
を書くのは大変です.そのため,エラーメッセージもマクロや関数形式マクロとして定義される場合があります.その際,__LINE__
を文字列に変換することも,しばしば行われるようです.
__LINE__
を文字列に変換するために,演算子#
が用いられます.#
は単項演算子のように用いられ,引数を文字列として返します.
#define _TOSTRING(NUMBER) #NUMBER
#define TOSTRING(NUMBER) _TOSTRING(NUMBER)
#define AT __FILE__ ":" TOSTRING(__LINE__)
#include <stdio.h>
int main(void)
{
printf("Fatal error at %s\n", AT);
return 0;
}
Fatal error at main.c:8
Fortranでの再現
Fortranでもマクロはそのまま利用できるので,再現するのは簡単です.
#define _TOSTRING(NUMBER) #NUMBER
#define TOSTRING(NUMBER) _TOSTRING(NUMBER)
#define AT __FILE__//":"//TOSTRING(__LINE__)
program main
implicit none
print '(A)', "Fatal error at "//AT
end program main
Fatal error at main.f90:7
ただし,このプログラムはIntel FortranおよびPGI Fortranではコンパイルできますが,gfortranではコンパイルエラーになります.stackoverflowにその理由や回避策を検討しているスレッドがあります2.gfortranはプリプロセスの際に#
演算子を認識しないので,__LINE__
が展開された際に,"7"
ではなく#7
となるためです.
>gfortran -cpp main.f90
main.f90:7:52:
print '(A)', "Fatal error at"//AT
1
Error: Syntax error in expression at (1)
cpp
を使ってプリプロセスを試みると,Fortranの文字列結合演算子//
がC++のコメントと認識されてしまいます.
>cpp -std=c89 main.f90
# 1 "main.f90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.f90"
program main
implicit none
print '(A)', "Fatal error at"main.f90:7:34: error: C++ style comments are not allowed in ISO C90
print '(A)', "Fatal error at"//AT
^
main.f90:7:34: error: (this will be reported only once per input file)
end program main
-std=c99
オプションを使うと,//
以降がコメントと認識され,破棄されます.破棄されないようにオプションを付けても,マクロではなくコメントと認識されているので,展開されません.
>cpp -std=c99 -CC main.f90
# 1 "main.f90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.f90"
program main
implicit none
print '(A)', "Fatal error at"//AT
end program main
実際にマクロを使う場面を想定すると,エラーや不整合の内容も併せて出力することになるので,文字列結合ができないのは少々厳しいと思われます.
関数を定義すればなんとかなりますが,__LINE__
を省略できず,冗長になります.
#define FILE __FILE__//":"
program main
implicit none
print '(A)', "Fatal error at "//FILE//toString(__LINE__)
contains
pure function toString(number) result(string)
use,intrinsic :: iso_fortran_env
implicit none
integer(int32),parameter :: MaxDigits = 10
integer(int32),intent(in) :: number
character(MaxDigits) string
write(string,'(I0)') number
end function toString
end program main
よい解決策をご存じの方はご教示ください.