C++ #if 書いて覚えるための初心者自己中記事
コンパイルするかしないかを制御する。というお話。
if (array == NULL) { throw invalid_argument("Average に変な引数が渡されました"); }
仮に、プログラムのどこかでエラー判定があったとして。
デバッグ中はこのままで、その後は必要ない。
という場合。
そんな場合にはassert マクロ が良いと前に勉強した。
~~~~~~~~~~~~~~~~~~~~
assertマクロについて
デバッグ中だけチェックしたいチェック文があったら使う。
if 文の代わりにassert ( ); に条件式を書く。
false の場合はエラーの発生したファイル名と番号がわかる。
#include <cassert> で使える。
そしてこのassert を使った分はまとめて消すことが出来る。
方法は#include <cassert> より先に
#define NDEBUG
を定義すること。
~~~~~~~~~~~~~~~~~~~~
これから勉強するもので
同じようなことが出来るらしい。
さっそく、
#if ! defined(NDEBUG) if (array == NULL) { throw invalid_argument("Average に変な引数が渡されました"); } #endif
先頭に # が付いた文が二つ、さっきの if 文を囲っています。
この #if というやつはそのあとに書かれた条件が真(true) の場合のみ #endif までの範囲をコンパイルする、というもの。
definedは #if 専用の演算子
defined の後のカッコに書かれたマクロが定義されているかを判定する。
この場合は ! が付いているのでNDEBUG というマクロが定義されていたら
コンパイルしない、となる。
よく似たもので二重インクルード防止コードがある。
#ifndef マクロ名
#define マクロ名
~~ヘッダファイルの内容~~
#endif
#ifndef は if not defined もし定義されていなければ
#ifdef は if defined もし定義されていればというのもある
つまり、さっきのコードだと ! で逆にしていたけれど
# if defined (マクロ名) だったが
#ifndef を使うとすっきりする。
#ifndef NDEBUG if (array == NULL) { throw invalid_argument("Average に変な引数が渡されました"); } #endif
このように定義されているかの判定が出来る。
さらに#if 関係は if なのでelse や else if なんかもある。
else は #else
else if は #elif
となる。
#ifdef マクロ名 ~~処理~~ #elif defined(マクロ名) ~~処理~~ #else ~~処理~~ #endif
#if の条件式で使用可能なのはコンパイル前に判明しているもの。
マクロや生の数値(即値というらしい) 。
#include <climits> #define UINT32_MAX 0xFFFFFFFF #if UINT_MAX == UINT32_MAX typedef unsigned int Uint32 #elif ULONG_MAX == UINT32_MAX typedef unsigned long Uint32 #elif USHRT_MAX == UINT32_MAX typedef unsigned short Uint32 #elif UCHAR_MAX == UINT32_MAX typedef unsigned char Uint32 #else #error 32ビットの符号なし型が存在しません #endif
はい、よくわからないの来ました。
#include <climits>
これは型の最大値のマクロが入っているんだろうなぁ。
#define UINT32_MAX 0xFFFFFFFF
これはなんだ・・?
記号定数の定義というやつなのね。
#define で UINT32_MAX という名前に0xFFFFFFFF という値に を付けている。
#if UINT_MAX == UINT32_MAX
ここで型の最大値との比較をしている。これはわかる。
typedef unsigned int Uint32
typedef は型に別名をつけるものだ。
UINT_MAXってのは unsigned int 型の最大値。
なので、unsigned int の最大値 == UINT32_MAX なのがtrue の時に
unsigned int に Uint32 という別名をつけたということか。
それを別の型でもやっているのね。
さいごに
#error 32ビットの符号なし型が存在しません
#error は強制的にコンパイルエラーを出すものらしい。
0xFFFFFFFF
この数値が上限の unsigned 型は32ビットなのか・・・。
どういうことだろう。
32ビットということは2進数だと32桁ということだよね。
4進数で16桁
8進数で8桁
16進数で4桁・・・?
いやいやいやいや、
とても勘違いだわ。恥ずかしい。
16進数の1桁は4ビットなのか。
8進数の1桁は3ビットね。
だから4ビット分のが8桁あるから32ビットか。
本筋と関係ない箇所でつまづく。
いや、テキストの序盤にちゃんと記載してあった。
話を戻して、32ビット符号なしの型を探して別名をつけるということをしているってことは、このコードの先ですでにその別名でいろいろやっているということか。
そんでコンパイルするときの環境に応じて32ビット符号なし型を適切に使うようにしていると。凄いなぁ。
こんなことが出来るなんて。
#ifdef NDEBUG #define HOGE(a, b) #else #define HOGE(a, b) Debug::Hoge(a, b) namespace Debug { void Hoge(int a, int b); } #endif
この場合は NDEBUG を判定してデバッグモードとリリースモードで
#define HOGE(a , b) の中身を変えている。
#ifdef NDEBUG #define DEBUG if(true){ }else #else #define DEBUG if(false){ }else #endif
こんな感じで中身の処理をさせるかさせないかとか。
#ifdef NDEBUG #define DEBUG(body) #else #define DEBUG(body) body #endif
本文で DEBUG( 全部の処理 )
でやれば、すべてを使うか使わないかが行える。
ここまで。