Edited at

便利かもしれないstatic_assert改


はじめに

static_assertのときに、ファイル名と行番号もついでに表示させられたら便利かもしれないというものです。

(2018/12/26 21:18 改訂 依存名改訂+α 書き換え箇所が多いです。すみませんorz

    もう一度目を通して頂ければ幸いです。(泣))

この記事は、boost pp のところだけみてください!!


必要なもの

C++17,boost


static_assert改

MAKE_COMPILE_ERRORマクロです。普通のstatic_assertとfalse_vを組み合わせ、ファイル名と行番号をメッセージと一緒に出力します。

#include <type_traits>

#include <boost/preprocessor/stringize.hpp>

#define MAKE_COMPILE_ERROR( ... ) \
static_assert([](auto && a) { \
return std::remove_reference<decltype(a)>::type::value; \
} (std::is_void<int>{}), \
"\n\n\nMAKED COMPILE ERROR!\n" \
"file: " BOOST_PP_STRINGIZE(__FILE__) "\n" \
"line: " BOOST_PP_STRINGIZE(__LINE__) "\n" \
"message: " BOOST_PP_STRINGIZE(#__VA_ARGS__) "\n\n" \
)


使い方

#include <string>

#include <type_traits>
#include <boost/preprocessor/stringize.hpp>

#define MAKE_COMPILE_ERROR( ... ) \
static_assert([](auto && a) { \
return std::remove_reference<decltype(a)>::type::value; \
} (std::is_void<int>{}), \
"\n\n\nMAKED COMPILE ERROR!\n" \
"file: " BOOST_PP_STRINGIZE(__FILE__) "\n" \
"line: " BOOST_PP_STRINGIZE(__LINE__) "\n" \
"message: " BOOST_PP_STRINGIZE(#__VA_ARGS__) "\n\n" \
)

class s {
int i;
std::string str;
public:
s() : i(10), str("default") {}
template<typename T>
T& get() {
if constexpr (std::is_same_v<int, T>) return i;
else if constexpr (std::is_same_v<std::string, T>) return str;
else MAKE_COMPILE_ERROR("テンプレート引数を確認してください");
}
};

int main() {
auto&& tmp = s{}.get<double>(); // コンパイルエラー
}

wandboxの出力例です。

.

.
.
prog.cc: In instantiation of 'T& s::get() [with T = double]':
prog.cc:29:34: required from here
prog.cc:8:23: error: static assertion failed:

MAKED COMPILE ERROR!
file: "prog.cc"
line: 24
message: "テンプレート引数を確認してください"

6 | static_assert([](auto && a) { \
| ~~~~~~~~~~~~~~~~~
.
.
.

コンパイルのエラーが少し見やすくなって、便利かもしれないです。


実装

テンプレート引数が与えられるまでstatic_assertの評価を延期させるために、下のコードをあたえました。std::remove_reference<decltype(a)>::type::valuestd::remove_referenceのテンプレートのパラメータに依存する名前なので、名前の検索は実際のテンプレート引数がわかるまで延期されます([temp.res]/9)。よって、if constexprでどれにもマッチしなかった時だけ、static_assertが評価されると考えられます。

[](auto && a) {

return std::remove_reference<decltype(a)>::type::value; // false
} (std::is_void<int>{})

(私がWandboxで試したときは、単純に[]{return false;}()だけでもgccとclang HEADで上と同じにできたので、そちらのほうがいいかもしれません...?) ←コンパイラのバグでした!バグじゃありませんでした!(2018/12/28 11:42)

表示するための文字列は、Boost.PPで作ってもらいました。

"\n\n\nMAKED COMPILE ERROR!\n" \

"file: " BOOST_PP_STRINGIZE(__FILE__) "\n" \
"line: " BOOST_PP_STRINGIZE(__LINE__) "\n" \
"message: " BOOST_PP_STRINGIZE(#__VA_ARGS__) "\n\n"

BOOST_PP_STRINGIZEは、文字列を作ってくれます。また、messageは__VA_ARGS__で受け取っているので、なんでも文字列として受け取れます。

MAKE_COMPILE_ERROR("default", hello! , 123, int);

// -> message: "\"default\", hello! , 123, int"


おわりに

ここまで読んでくれたあなたに感謝を!