読者です 読者をやめる 読者になる 読者になる

【初心者C++er Advent Calendar 2016 21日目】C++ でプリプロセスマクロを使う場面

初心者C++er Advent Calendar 2016 21日目の記事です。
さて、巷ではマクロを使用したコードを公開すると『static const を使え』や『typedef しろ』、『関数テンプレートでおk』などと斧が飛んでくる突っ込まれることが多いです。
これは C++ では『基本的にはマクロを使うべきではなく』さらに『マクロを使わないでよいコード』に直せることが多いからです。
では逆に『マクロを使うべきコード』というのはどういうものなのでしょうか。
この記事では『マクロを使うべきコード』の実例をいくつか紹介してみたいと思います。

[インクルードガード]

ヘッダーファイルを記述する際に『重複インクルードを防止するため』にインクルードガードという技法が使用されれます。
これは #ifndef を利用して『一度だけヘッダーファイルが読み込まれる』ような仕組みです。

// hoge.h
#ifndef HOGE_H
#define HOGE_H

// ここに実装を記述する

#endif    // HOGE_H

これにより複数回 #include "hoge.h" しても一度だけ定義が読み込まれます。
ただ、最近は同等の機能である #pragma once を実装してるコンパイラもあるので、そちらを使用している人もいます。

[キーワードなどを置き換える]

さて、次のように constexpr を使ったコードがあるとします。

constexpr auto
plus(int a, int b){
    return a + b;
}

constexpr auto
minus(int a, int b){
    return a - b;
}

static_assert(plus(1, 2) == 3, "");
static_assert(minus(4, 5) == -1, "");

これは C++11 に対応してるコンパイラであれば問題なく動作します。
しかし、環境によってはどうしても C++03 に対応しなければならないこともあります。
こういう場合、constexpr というキーワードをマクロに置き換えることで、複数の環境に柔軟に対応する事が出来ます。

// C++11 以上ならば
#if __cplusplus >= 201103L
    // constexpr をマクロに置き換える
   #define CONSTEXPR constexpr
#else
    // C++11 以上に対応してないなら空のマクロを定義
   #define CONSTEXPR
#endif

// constexpr をマクロに置き換える
CONSTEXPR int
plus(int a, int b){
    return a + b;
}

CONSTEXPR int
minus(int a, int b){
    return a - b;
}

#if __cplusplus >= 201103L
static_assert(plus(1, 2) == 3, "");
static_assert(minus(4, 5) == -1, "");
#endif

このように『マクロ以外』で置き換えることが出来ないようなワークアラウンドに対応する場合に利用できます。

[コンパイルするときに挙動を変更したい場合]

例えば、次のように『デバッグコードを仕込んだコード』があるとします。

int
plus(int a, int b){
// DEBUG が定義されている時のみログを出力したい
#ifndef DEBUG
    std::cout << a << std::endl;
    std::cout << b << std::endl;
#endif
    return a + b;
}

このように『マクロを使用すること』でコンパイル時に『挙動を変更する』事が出来ます

# DEBUG マクロを定義してコンパイルする
$ g++ main.cpp -DDEBUG
$ ./a
1
2
3

gcc だとこんな感じでコンパイル時にマクロを定義する事が出来ます。

[まとめ]

と、言う感じでパッと思いついたものを上げてみました。
他にも使うべき場面はありますが、初心者向けならこのあたりでしょうか。
C++ では『マクロを使うべきではないコード』もたくさんありますが『マクロでしか実装出来ないコード』もまた存在します。
マクロは用法・用量を守って正しくお使いください。