はじめに
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::value
はstd::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"
おわりに
ここまで読んでくれたあなたに感謝を!