8
5

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 3 years have passed since last update.

FortranAdvent Calendar 2019

Day 22

FortranでC言語の__FILE__, __LINE__マクロを使う

Last updated at Posted at 2020-01-02

概要

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__は整数に展開されます.簡単なプログラムを例示します.

main.c
#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でもマクロはそのまま利用できるので,再現するのは簡単です.

main.f90
#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

よい解決策をご存じの方はご教示ください.

  1. https://gcc.gnu.org/onlinedocs/gfortran/Preprocessing-Options.html

  2. https://stackoverflow.com/questions/31649691/stringify-macro-with-gnu-gfortran

8
5
2

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?