ぷよぐらみんぐ勉強

勉強したやつとか疑問などを載せていきます。割りと雑に。

assertの式を理解していく。

glfwとglewを使ってOpenGLのライブラリを作っているのですが

  1. assertと同じ形式
  2. ReleaseではMessageBoxを表示してログ出力後終了
  3. Debugでは普通のassert実行

という条件を持つassertマクロが欲しくなってきました。
元のassertではReleaseの際には無視されるのでログとして出力したいのが一番・・・
ですがassert文の認識が「この形式でやればいい」という浅い認識で使っていたので、このままじゃ作れもしないのでこの際理解しておこうと思い調べました。

  • まずinclude内にて定義されているdebug時のassert
#define assert(expression) (void)((!!(expression))||(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0)

C++初心者の身としては割りと複雑に見える印象。
とりあえず少しずつ分解して理解していきます。マクロ構文もあまり覚えていませんしね・・・

C++、Cの仕様を把握していない状態なので、間違いがあったりするかもしれないので、
ありましたら指摘お願いします。

まずはじめに

(!!(expression))の!!について

どうやら

  • 論理否定で0か1で返すことが保証される。

というのを利用している。
論理否定は主に真偽結果に対して使っているけどintなどに使っても問題はないと。
ただ、C#はこんな変換は許してくれませんね・・・まあ無理矢理感があるので使いませんが。

処理の流れとしては
1回目で0か1に変換、2回目で逆転した真偽判定を戻すという感じ。

(!!(expression))の引数

assertに渡す基本形が
変数 && "test"とかですが、なぜbool判定ができるんだという疑問がありました。
で、文字列の方ポインタだということに気が付きました。
ポインタなら普通に0以外返ってきますし・・・・
まだポインタ慣れしていない証拠ですね。

最初の(void)

これはRelease時のassertの実装と同じで無視する対象になる。

_wassertにある #マクロ引数

完全に入門の内容・・・

#をつけると引数の変数名や文字列がそのまま文字列になる。
例として渡した引数が

false && "s"

だと出力時にまんまこれが表示される。
がそのまま表示される。assertの出力結果を見るとまんま表示されてますね。

(メソッド(), 0) 構文

演算子の項目を見るとカンマ演算子なるものがありました。
カンマ演算子
>|cpp|

int test = (12,23,34,4);
std::cout << test << std::endl;

|

これで表示されるのは4です。
()内の一番右にある数値が代入される模様。
そして数値以外にも式やメソッドが指定可能です。

  • 式の場合
int test = 0;
int test2 = 10;
test += (test += test2, test += 20);
std::cout << test << std::endl;

f:id:himigami:20170119145313p:plain
この場合testの値は60となりました。
処理順はtest2を加算、20を加算、加算された合計を更に加算

  • メソッド
void show()
{
    std::cout << "show method" << std::endl;
}

int main()
{
    int test = 0;[f:id:himigami:20170119145226p:plain]
    test = (show(), test += 20, 0);
    std::cout << test << std::endl;
    return 0;
}

結果はこちら
f:id:himigami:20170119145226p:plain
メソッドが実行され、加算がされたあと0を代入という結果。
動作を見る限り式とメソッドは必ず行うみたいですね。
もちろんメソッドの戻り値も利用可能です。
他には()がなければ演算子として認識はされませんでした。
どうやらカンマ演算子演算子の中で一番優先度の低いものなので認識されなかったと。

このことから
assertでは(メソッド, 0)を実行して戻ってくるのは0。
ここのメソッドで色々指定しとけばOKということですね。

処理のまとめ

  1. (!!(引数))でbool判定。もしfalseであれば次へ
  2. (メソッド, 0)でメソッド実行後0を返す。
  3. (void)にしているため受け取る必要はない。

こんな感じですね。

その結果できたコードがこちら。命名は適当にしてるので変更予定
についてはassertのやつをまんまコピー
f:id:himigami:20170119150404p:plain
f:id:himigami:20170119152458p:plain

今回調べたものはC++、Cの入門のものしかなかったので細かいところ見てないんだなって思いました。
C++er、Cerの人たちはこれを軽々扱うのが怖い...


あと・・・保存用として・・・

別枠 マクロ __FILE__ と __LINE__

__FILE__はファイル名(フルパス)
__LINE__はファイルの行番号
を表します。