LoginSignup
3
3

More than 3 years have passed since last update.

includeディレクティブは3種類あった

Last updated at Posted at 2020-10-10

お久しぶりの初心者Cプログラマーです。いつから初心者を脱するのか不明ですが初心者です。今回ちょっとした小ネタ投稿。

#includeディレクティブ? 2種類でしょ?

Cプログラマーであればおよそ誰もが使う1#include。概ね以下の2つであろうと思います。

includeは2種類
// <>で囲むやつ
#include <stdio.h>

// ""で囲むやつ
#include "private_use.h"

はい、普通これでOKですが、第3の形式があります。

マクロ展開によるパターン増

それがこれです。

includeパターン3
#define HEADER_MACRO "switched.h"
#include HEADER_MACRO

すべてプリプロセス時点で完結するからできるのかもしれませんが、こんな誰が使うのか分からない指定ができるわけです。

どうやって使うのか?

具体的に何ができるかというと、以下のようなことができます。

include_ver1.h
#ifndef INCLUDE_VER1_H_INCLUDE
#define INCLUDE_VER1_H_INCLUDE

#include <stdio.h>

#define MSG_FUNC(fmt, ...) printf("LOG ver1: " fmt "\n", ##__VA_ARGS__)

#endif
include_ver2.h
#ifndef INCLUDE_VER2_H_INCLUDE
#define INCLUDE_VER2_H_INCLUDE

#include <stdio.h>

#define MSG_FUNC(fmt, ...) printf("LOG ver2: " fmt "\n", ##__VA_ARGS__)

#endif
include_test.c
#ifdef USE_VER1
    #define INCLUDE_FILE "include_ver1.h"
#else
    #define INCLUDE_FILE "include_ver2.h"
#endif

#include INCLUDE_FILE

int main(void)
{
    MSG_FUNC("use header %s", INCLUDE_FILE);
}

見れば分かるかと思いますが、USE_VER1が定義されていればinclude_ver1.hを、そうでなければinclude_ver2.hをインクルードしようとしています。これが機能すれば、USE_VER1定義ありの場合はMSG_FUNCで"LOG ver1: "が、定義なしなら"LOG ver2: "が出力されるはずです。

手元のWSL2/ubuntu20.04環境で実行したところ、以下のような実行結果が得られました。

実行結果
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ clang --version
clang version 10.0.0-4ubuntu1 
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ gcc -Wall -Wextra include_test.c 
$ ./a.out 
LOG ver2: use header include_ver2.h
$ gcc -Wall -Wextra -D USE_VER1 include_test.c 
$ ./a.out 
LOG ver1: use header include_ver1.h
$ clang -Wall -Wextra include_test.c 
$ ./a.out 
LOG ver2: use header include_ver2.h
$ clang -Wall -Wextra -D USE_VER1 include_test.c 
$ ./a.out 
LOG ver1: use header include_ver1.h

適用されていそうです。

それC言語じゃなくてプリプロセッサが気を利かせただけじゃないの?

と我に返る方もいるでしょうから、規格を見ましょう。いつものn1570(C11のdraft)2です。

6.10.2-4
A preprocessing directive of the form
# include pp-tokens new-line
(that does not match one of the two previous forms) is permitted. The preprocessing tokens after include in the directive are processed just as in normal text. (Each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens.) The directive resulting after all replacements shall match one of the two previous forms. ...

拙訳:
# include プリプロセストークン 改行
この形式の(前の2形式のいずれにも該当しない)プリプロセス命令が許容される。命令内でincludeの後に置かれるプリプロセストークンは、通常のテキストのようにプロセスされる。(それぞれ今マクロ名として定義されている識別子はプリプロセストークンのリストで置換される。)置換の結果最終的に生成された命令は前の2形式のいずれかに合致せねばならない。(後略)

これを見る限り、C言語仕様の範疇ですね。実際、6.10.2-8に例まで載せて説明してくれています。

規格記載の例
#if VERSION == 1
#define INCFILE "vers1.h"
#elif VERSION == 2
#define INCFILE "vers2.h" // and so on
#else
#define INCFILE "versN.h"
#endif
#include INCFILE

ちなみに、n4659(C++17のdraft)3ではどうかというと、19.2-4にほぼ同様の内容があります。

最後に

ぶっちゃけ使う機会がないですが、 久々に調べていて楽しかったです。


  1. 古のCなら標準の関数その他をexternしてた、とかの突っ込みはナシで……。 

  2. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 

  3. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf 

3
3
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
3