2016年6月24日金曜日

Custom assert macro

少し前からホビープログラムをC++主体に戻した。
1x系は刺激的で楽しい。

そんな刺激的な中で何も変わっていないものが普通の assert。
コンパイルタイムで利用できる static_assert が追加され、メッセージも別途出力できるのに
こいつだけは何も変わっていない!

assert(false && "hoge");
assert(!"foo");

泥臭くていやなんです。
ということで、何番煎じかは置いといて「無ければ作ればよろし」。

前提としては、
(1) メモリがシビアではない環境
(2) メッセージの別途出力(stream風に記述したい)
(3) リリースモードで一命令も吐かせない
(4) リリースモードでコンパイラを黙らせる(C4101対策)

bool cond = false;
int value = 10;
assert(cond, "hoge: " << value);

上記のような感じが目標。

#pragma once
#include <iostream>
#include <sstream>

#undef ASSERT_MSG
#undef ASSERT

#ifdef NDEBUG
 #define ASSERT_MSG(expression, message)\
    do{\
        (void)(true ? (void)0 : ((void)(expression)));\
        (void)(true ? (void)0 : ((void)(std::ostringstream() << message)));\
    }while(false)
 #define ASSERT(expression)\
    ASSERT_MSG(expression, "")
#else
 #define ASSERT_MSG(expression, message)\
    do{\
        if(!(expression)){\
            std::cerr << "Assertion `" #expression "` failed in " << __func__ << "() " << __FILE__ << "(" << __LINE__ << "): " << message << std::endl;\
            std::terminate();\
        }\
    }while(false)
 #define ASSERT(expression)\
    ASSERT_MSG(expression, "")
#endif

そしてこんな感じ。

bool cond = false;
int value = 10;
ASSERT_MSG(cond, "hoge" << value);

STL(std::ostringstream())はコンパイルタイムで対処しないとコードが生成されるため
当初は、(void)sizeof() などを利用したが、その他のケースを両立できないため
シンプルにコンパイラに丸投げすることに。

msvs2015環境では前提条件をすべて満たしたことは確認。
コンパイラさんの最適化恐るべし。