Loose-Info.com
Last Update 2024/01/09
TOP - 各種テスト - C++ - プリプロセッサ指令

概要

#include
#define
#define (引数付き)
#演算子 (文字列化演算子)
##演算子 (トークン連結演算子)
#undef
#if #elif #else #endif
#ifdef #ifndef
#line
#error
#pragma
_Pragma演算子
defined演算子
定義済みマクロ(実装依存値含む)


#include


sample.cpp
/* 実装で提供される標準ライブラリヘッダファイル iostream の読み込み */ #include <iostream> /* プログラム特有のソースファイル sample.h の読み込み */ #include "sample.h" int main() { Smpl s = {1, 2}; std::cout << "s.i = " << s.i << std::endl; std::cout << "s.j = " << s.j << std::endl; }
sample.h
struct Smpl { int i; int j; };

-Eオプションでプリプロセス実行済みのコードをファイルに出力
$ gcc -Wall -E sample.cpp -lstdc++ > sample_e.txt

出力されたsample_e.txt
# 0 "sample.cpp" # 0 "<built-in>" # 0 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 0 "<command-line>" 2 # 1 "sample.cpp" # 1 "/usr/include/c++/12.2.0/iostream" 1 3 <--- 「iostream」 # 36 "/usr/include/c++/12.2.0/iostream" 3 # 37 "/usr/include/c++/12.2.0/iostream" 3 # 1 "/usr/include/c++/12.2.0/x86_64-pc-linux-gnu/bits/c++config.h" 1 3 # 296 "/usr/include/c++/12.2.0/x86_64-pc-linux-gnu/bits/c++config.h" 3 # 834 "/usr/include/c++/12.2.0/ostream" 2 3 # 40 "/usr/include/c++/12.2.0/iostream" 2 3 # 1 "/usr/include/c++/12.2.0/istream" 1 3 # 1017 "/usr/include/c++/12.2.0/istream" 2 3 # 41 "/usr/include/c++/12.2.0/iostream" 2 3 namespace std __attribute__ ((__visibility__ ("default"))) { # 60 "/usr/include/c++/12.2.0/iostream" 3 extern istream cin; extern ostream cout; extern ostream cerr; extern ostream clog; extern wistream wcin; extern wostream wcout; extern wostream wcerr; extern wostream wclog; static ios_base::Init __ioinit; } # 3 "sample.cpp" 2 # 1 "sample.h" 1 # 1 "sample.h" <--- 「sample.h」 struct Smpl { int i; int j; }; # 6 "sample.cpp" 2 int main() { Smpl s = {1, 2}; std::cout << "s.i = " << s.i << std::endl; std::cout << "s.j = " << s.j << std::endl; }

-Eオプションでプリプロセス実行済みのコードをファイルに出力
$ gcc -Wall -x c++ sample_e.txt -lstdc++ $ ./a.out s.i = 1 s.j = 2

#define


sample.cpp
#include <iostream> /* マクロ名を文字列として定義 */ #define SAMP_MACRO1 "abcdef" /* マクロ名を数式として定義 */ #define SAMP_MACRO2 (1 * 2) /* 置換テキストを空で定義 */ #define SAMP_MACRO3 /* マクロ名をコマンド行として定義 */ #define SAMP_MACRO4 std::cout << "SAMP_MACRO4" << std::endl; int main() { std::cout << "SAMP_MACRO1 = " << SAMP_MACRO1 << std::endl; std::cout << "SAMP_MACRO2 = " << SAMP_MACRO2 << std::endl; SAMP_MACRO3 SAMP_MACRO4 }

-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.cpp -lstdc++ # 15 "sample.cpp" # 15 "sample.cpp" int main() { std::cout << "SAMP_MACRO1 = " << "abcdef" << std::endl; <--- 「SAMP_MACRO1」は「"abcdef"」に置換 std::cout << "SAMP_MACRO2 = " << (1 * 2) << std::endl; <--- 「SAMP_MACRO2」は「(1 * 2)」に置換 <--- 「SAMP_MACRO3」は空 std::cout << "SAMP_MACRO4" << std::endl; <--- 「SAMP_MACRO4」は「std::cout << "SAMP_MACRO4" << std::endl;」に置換 }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out SAMP_MACRO1 = abcdef SAMP_MACRO2 = 2 SAMP_MACRO4

#define (引数付き)


sample.cpp
#include <iostream> /* 引数付きマクロ */ #define SAMP_MACRO1(str1) std::cout << "str1 = " str1 << std::endl; /* 可変個数引数付きマクロ(識別子__VA_ARGS__使用) */ #define SAMP_MACRO2(...) v(__VA_ARGS__); /* 2つの引数の演算式を置換テキストとして定義 */ #define SAMP_MACRO3(x, y) (x * y) /* 2つの引数をそれぞれ括弧で括った演算式を置換テキストとして定義 */ #define SAMP_MACRO4(x, y) ((x) * (y)) static int n = 0; // カウント用 template<class T> void v(T x) { std::cout << "Type = " << typeid(T).name() << " --- args[" << n << "] = " << x << std::endl; n++; } template<class T, class... Args> void v(T x, Args... args) { v<T>(x); v<Args...>(args...); } int main() { int a = 2; int b = 3; SAMP_MACRO1("abc") SAMP_MACRO2(1, 2, 1.23, 'a', 1.23456789012345678L) std::cout << "SAMP_MACRO3 = " << SAMP_MACRO3(a, b) << std::endl; std::cout << "SAMP_MACRO3 = " << SAMP_MACRO3(a + b, b + 3) << std::endl; std::cout << "SAMP_MACRO4 = " << SAMP_MACRO4(a + b, b + 3) << std::endl; }

-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.cpp -lstdc++ # 15 "sample.cpp" # 15 "sample.cpp" static int n = 0; template<class T> void v(T x) { std::cout << "Type = " << typeid(T).name() << " --- args[" << n << "] = " << x << std::endl; n++; } template<class T, class... Args> void v(T x, Args... args) { v<T>(x); v<Args...>(args...); } int main() { int a = 2; int b = 3; std::cout << "str1 = " "abc" << std::endl; <--- 引数「str1」は「"abc"」に置換 v(1, 2, 1.23, 'a', 1.23456789012345678L); <--- 可変個数引数はそれぞれ置換 std::cout << "SAMP_MACRO3 = " << (a * b) << std::endl; <--- 引数aとbが式中で置換 std::cout << "SAMP_MACRO3 = " << (a + b * b + 3) << std::endl; <--- 式中で置換された際に括弧が無いためb * bの演算が先行される std::cout << "SAMP_MACRO4 = " << ((a + b) * (b + 3)) << std::endl; <--- それぞれの引数に括弧がある場合、マクロの意図する演算順序となる }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out str1 = abc Type = i --- args[0] = 1 Type = i --- args[1] = 2 Type = d --- args[2] = 1.23 Type = c --- args[3] = a Type = e --- args[4] = 1.23457 SAMP_MACRO3 = 6 SAMP_MACRO3 = 14 SAMP_MACRO4 = 30

#演算子 (文字列化演算子)


sample.cpp
#include <iostream> /* # (文字列化)演算子(1) */ #define SAMP_MACRO1(str1, str2) #str1 " " #str2 /* # (文字列化)演算子(2) (識別子__VA_ARGS__使用) */ #define SAMP_MACRO2(...) #__VA_ARGS__ /* # (文字列化)演算子(3) */ #define SAMP_MACRO3(str1) std::cout << #str1 " = " << str1 << std::endl int main() { int a = 1; int b = 2; std::cout << SAMP_MACRO1(2*3, 4*5) << std::endl; std::cout << SAMP_MACRO2(2*3, 4-5, 6+7) << std::endl; SAMP_MACRO3(a + b); }

-Eオプションを付けてコンパイルした場合の出力結果
$ gcc -Wall -E sample.cpp -lstdc++ # 12 "sample.cpp" # 12 "sample.cpp" int main() { int a = 1; int b = 2; std::cout << "2*3" " " "4*5" << std::endl; <--- 「#」の付いた引数がそれぞれ二重引用符で囲まれて置換 std::cout << "2*3, 4-5, 6+7" << std::endl; <--- 可変個数引数全体が二重引用符で囲まれて置換 std::cout << "a + b" " = " << a + b << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 2*3 4*5 2*3, 4-5, 6+7 a + b = 3

##演算子 (トークン連結演算子)


sample.cpp
#include <iostream> #define A1 "sample1" #define A2 "sample2" #define A3 "sample3" /* ## (トークン連結)演算子 */ #define SAMP_MACRO(str1, str2) str1 ## str2 int main() { std::cout << SAMP_MACRO(A, 1) << std::endl; std::cout << SAMP_MACRO(A, 2) << std::endl; std::cout << SAMP_MACRO(A, 3) << std::endl; }

-Eオプションを付けてコンパイルした場合の出力結果
bash-5.1$ gcc -Wall -E sample.cpp -lstdc++ # 10 "sample.cpp" # 10 "sample.cpp" int main() { std::cout << "sample1" << std::endl; 「A ## 1」 ---> 「A1」 ---> 「"sample1"」 std::cout << "sample2" << std::endl; 「A ## 2」 ---> 「A2」 ---> 「"sample2"」 std::cout << "sample3" << std::endl; 「A ## 3」 ---> 「A3」 ---> 「"sample3"」 }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out sample1 sample2 sample3

#undef


sample.cpp
#include <iostream> /* 最初のマクロ定義 */ #define SAMP_MACRO "sample_1" int main() { std::cout << SAMP_MACRO << std::endl; /* マクロ取り消し */ #undef SAMP_MACRO /* マクロ再定義 */ #define SAMP_MACRO "sample_2" std::cout << SAMP_MACRO << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out sample_1 sample_2

#if #elif #else #endif


sample.cpp
#include <iostream> #define SMPL_MACRO1 2 #define SMPL_MACRO2 20 int main() { /* 条件コンパイル */ #if defined(__GNUC__) # if __GNUC__ >= 9 std::cout << "__GNUC__ = " << __GNUC__ << std::endl; # endif #endif #if SMPL_MACRO1 > 2 /* この条件はコンパイルされない */ std::cout << "SMPL_MACRO1 > 2" << std::endl; #else # if SMPL_MACRO2 > 20 /* この条件はコンパイルされない */ std::cout << "(SMPL_MACRO1 <= 2) && (SMPL_MACRO2 > 20)" << std::endl; # elif SMPL_MACRO2 == 20 std::cout << "(SMPL_MACRO1 <= 2) && (SMPL_MACRO2 == 20)" << std::endl; # endif #endif }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out __GNUC__ = 12 (SMPL_MACRO1 <= 2) && (SMPL_MACRO2 == 20)

#ifdef #ifndef


sample.cpp
#include <iostream> #define SMPL_MACRO1 2 /* 置換テキストが空のマクロを定義 */ #define SMPL_MACRO2 int main() { /* __GNUC__が定義されていることを確認 */ #ifdef __GNUC__ std::cout << "__GNUC__ = " << __GNUC__ << std::endl; #endif /* SMPL_MACRO1が定義されていることを確認 */ #ifdef SMPL_MACRO1 /* SMPL_MACRO2が定義されていることを確認 */ # ifdef SMPL_MACRO2 /* SMPL_MACRO3が定義されていないことを確認 */ # ifndef SMPL_MACRO3 std::cout << "#ifdef SMPL_MACRO1 ---> #ifdef SMPL_MACRO2 ---> #ifndef SMPL_MACRO3" << std::endl; # endif # endif #endif }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out __GNUC__ = 12 #ifdef SMPL_MACRO1 ---> #ifdef SMPL_MACRO2 ---> #ifndef SMPL_MACRO3

#line


sample1.cpp
#include <iostream> extern void func(void); int main() { /* 標準定義済みマクロ */ /* __FILE__ : 現在のファイル名 */ /* __LINE__ : 現在の行番号 */ #line 1 /* 次行を関数main()の1行目として定義 */ std::cout << __FILE__ << " main() " << __LINE__ << "行目" << std::endl; std::cout << __FILE__ << " main() " << __LINE__ << "行目" << std::endl; func(); #line 356 "/usr/include/stdio.h" std::cout << __FILE__ << " stdio.h " << __LINE__ << "行目" << std::endl; }
sample2.cpp
#include <iostream> void func() { #line 1 /* 次行を関数func()の1行目として定義 */ std::cout << __FILE__ << " func() " << __LINE__ << "行目" << std::endl; }

実行結果
$ gcc -Wall sample1.cpp sample2.cpp -lstdc++ $ ./a.out sample1.cpp main() 1行目 sample1.cpp main() 2行目 sample2.cpp func() 1行目 /usr/include/stdio.h stdio.h 356行目

#error


sample.cpp
#include <iostream> int main() { #if defined(__GNUC__) # if __GNUC__ >= 9 /* 次行でエラー発生・処理停止 */ #error "__GNUC__ >= 9" # endif #endif std::cout << "sample" << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ sample.cpp:8:2: error: #error "__GNUC__ >= 9" <--- この時点で処理停止 8 | #error "__GNUC__ >= 9" | ^~~~~

#pragma


sample.cpp
#include <iostream> int main() { /* コンパイラメッセージとして文字列を表示 */ #pragma message "sample message" /* 警告メッセージを生成 */ #pragma GCC warning "sample warning" std::cout << "sample" << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ sample.cpp:9:21: warning: sample warning 9 | #pragma GCC warning "sample warning" | ^~~~~~~~~~~~~~~~ sample.cpp: In function ‘int main()’: sample.cpp:6:17: note: ‘#pragma message: sample message’ 6 | #pragma message "sample message" | ^~~~~~~~~~~~~~~~ $ ./a.out sample

_Pragma演算子


sample.cpp
#include <iostream> /* _Pragma演算子をマクロで使用 */ #define WARNING_STR2(s) PRAGMA_WARNING(GCC warning #s) #define PRAGMA_WARNING(s) _Pragma(#s) int main() { /* #pragmaで直接記述 */ #pragma GCC warning "sample warning 0" /* _Pragma演算子で記述 */ _Pragma ("GCC warning \"sample warning 1\"") /* _Pragma演算子を使用したマクロで記述 */ WARNING_STR2(sample warning 2) std::cout << "sample" << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ sample.cpp:10:21: warning: sample warning 0 10 | #pragma GCC warning "sample warning 0" | ^~~~~~~~~~~~~~~~~~ sample.cpp:13:13: warning: sample warning 1 13 | _Pragma ("GCC warning \"sample warning 1\"") | ^~~~~~~~~~~~~~~~~~ sample.cpp:16:13: warning: sample warning 2 16 | WARNING_STR2(sample warning 2) | ^~~~~~~~~~~~~~~~~~ $ ./a.out sample

defined演算子


sample.cpp
#include <iostream> #define SMPL_MACRO1 2 /* 置換テキストが空のマクロを定義 */ #define SMPL_MACRO2 int main() { /* __GNUC__が定義されていることを確認 */ #if defined(__GNUC__) std::cout << "__GNUC__ = " << __GNUC__ << std::endl; #endif /* SAMP_MACRO1とSAMP_MACRO2が定義されていることを確認 */ #if defined(SMPL_MACRO1) && defined(SMPL_MACRO2) std::cout << "#if defined(SMPL_MACRO1) && defined(SMPL_MACRO2)" << std::endl; #endif }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out __GNUC__ = 12 #if defined(SMPL_MACRO1) && defined(SMPL_MACRO2)

定義済みマクロ(実装依存値含む)


sample.cpp
#include <iostream> int main() { std::cout << "定義済みマクロ(実装依存値含む)" << std::endl; std::cout << "コンパイラがC++のときに定義 __cplusplus = " << __cplusplus << std::endl; std::cout << "現在のソースファイルのプリプロセス実行の日付 __DATE__ = " << __DATE__ << std::endl; std::cout << "現在のソースファイル名 __FILE__ = " << __FILE__ << std::endl; std::cout << "ソースファイル内の「現在の」行番号 __LINE__ = " << __LINE__ << std::endl; std::cout << "現在のソースファイルのプリプロセス実行時刻 __TIME__ = " << __TIME__ << std::endl; }

実行結果
$ gcc -Wall sample.cpp -E -lstdc++ > sample_e.txt <--- プリプロセスの実行結果をファイルへ出力 $ cat sample_e.txt <--- 出力結果の表示 # 2 "sample.cpp" 2 # 3 "sample.cpp" int main() { std::cout << "定義済みマクロ(実装依存値含む)" << std::endl; std::cout << "コンパイラがC++のときに定義 __cplusplus = " << 201703L << std::endl; std::cout << "現在のソースファイルのプリプロセス実行の日付 __DATE__ = " << "Dec 30 2023" << std::endl; std::cout << "現在のソースファイル名 __FILE__ = " << "sample.cpp" << std::endl; std::cout << "ソースファイル内の「現在の」行番号 __LINE__ = " << 9 << std::endl; std::cout << "現在のソースファイルのプリプロセス実行時刻 __TIME__ = " << "22:39:51" << std::endl; } $ gcc -Wall -x c++ sample_e.txt -lstdc++ <--- 出力結果を使用してコンパイルを引き続き実行 $ ./a.out 定義済みマクロ(実装依存値含む) コンパイラがC++のときに定義 __cplusplus = 201703 現在のソースファイルのプリプロセス実行の日付 __DATE__ = Dec 30 2023 現在のソースファイル名 __FILE__ = sample.cpp ソースファイル内の「現在の」行番号 __LINE__ = 9 現在のソースファイルのプリプロセス実行時刻 __TIME__ = 22:39:51

実行環境

GNU bash, version 5.1.16
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39


コード例・出力内容中の表記

・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。