コンパイル時/実行時両用アサートについて考える
SPROUT_ASSERTは非常に優れており、コンパイル時、実行時両方で使えます。
しかし、以下のようなコードはコンパイルエラーにはならず、実行時に初めてアサーションに失敗します。
constexpr auto i = 0; SPROUT_ASSERT(i == 1);
一方、static_assertを用いると以下のようなコードをコンパイルエラーに出来ますが、実行時両用ではありません。
constexpr auto i = 0; static_assert(i == 1);
次世代アサートに期待するものはコンパイル時 / 実行時両方で使え、尚且つコンパイル時に結果が分かるものは全てコンパイルエラーにする機能です。
そこで以下のようなアサーションを考えてみました。
#include <cassert> #include <boost/preprocessor/cat.hpp> #define my_assert(x, y) \ using BOOST_PP_CAT(my_static_assert, __LINE__) = int[x ? 0 : -1];\ using BOOST_PP_CAT(my_cassert, __LINE__) = int[x ? 0 : (assert((#y, false)), 1)];
型を定義する事でアサートを行います。
アサーションに失敗すると、型定義に失敗するようになっており、結果としてコンパイルエラーになります。
実行時はcassertに処理を丸投げしています。
このアサートを用いると以下のようなコードはコンパイル時に失敗します。
constexpr auto i = 1; my_assert(i == 0, "Asssertion Error");
コンパイラの出力(clang3.4の場合)
test.cpp:11:15: error: array size is negative
my_assert(i == 0, "Asssertion Error");
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:4:30: note: expanded from macro 'my_assert'
using my_static_assert12 = int[x ? 0 : -1];\
^
1 error generated.
次に実行時の例です
auto i = 1; my_assert(i == 0, "Asssertion Error");
出力
Assertion failed: ( ( "\"Asssertion Error\"", false ) ), function main, file test.cpp, line 11.
[1] 4008 abort ./a.out
いずれも"Asssertion Error"というメッセージを含んでいます。
問題はコンパイル時のエラーメッセージが実質array size is negativeとなってしまうこと。
行番号を利用しているので同じ行で2つ以上使えない、また、使う度にmy_assert番号系の型が増えること。
ローカルスコープを作ってその中で定義する({}で囲む)というのも考えたのですが、グローバルスコープで使えなくなるので単に使用者側が改行すれば済む(面倒ではあります)行番号を結合することにしました。
コンパイル時に評価した場合コンパイルエラーに出来る上に、実行時にも使えるものをネガティブサイズの配列以外に探したいですね。
便利なアサートの実装は難しいです…。