はじめに
Visual Studio も最近はだいぶC++標準への準拠が進んでいて、Visual Studio "15" Preview 5ではC++14のconstexprも使えるそうですね。
しかしまだVisual Studio 2013のサポートは切れない、というプロジェクトも多いハズ。
ところで、C++11でstd::error_code
, std::error_category
, std::system_category()
, std::system_error
といったクラス・関数が追加され、Win32APIのエラーコードの文字列化にFormatMessage
を自力で呼び出さなくて良くなりました。
FormatMessageを呼ばなくていい素晴らしい世界
using std::system_category;
using std::system_error;
const auto er = RegOpenKeyEx(parent_key_handle, sub_key_root, 0, r, &this->key);
if (ERROR_SUCCESS != er) {
throw system_error(std::error_code(er, system_category()), "RegOpenKeyEx:(" + std::to_string(er) + ')');
}
でもね、それVS2013では使えないんだ
残念ながらVS2013では実装が途中で使い物になりません。
いや、まあVS2015でもぶっ壊れてるじゃねーか!という話もありますが
VS2015のstd::error_categoryが奇想天外な件について
規格に沿っていないというだけで動作的には期待したものですしお寿司。
じゃあ自作しよう
#include <Windows.h>
#include <string>
#include <stdexcept>
#include <system_error>
#if defined(_MSC_VER) && _MSC_VER < 1900
std::string format_message(DWORD lasterr) {
char* buf = nullptr;
const DWORD len = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
nullptr,
lasterr,
LANG_NEUTRAL,
reinterpret_cast<LPSTR>(&buf),
0,
nullptr
);
DWORD i = (len < 3) ? 0 : len - 3;
for (; '\r' != buf[i] && '\n' != buf[i] && '\0' != buf[i]; i++);//改行文字削除
buf[i] = '\0';
std::string ret;
try{
ret = buf;//エラーメッセージをコピー
}
catch(...){//ここで例外を投げるよりはエラーメッセージなしのほうが良いので握りつぶす
}
LocalFree(buf);//FormatMessageAでFORMAT_MESSAGE_ALLOCATE_BUFFERを指定したので必ず開放
return ret;
}
using std::error_category;
class system_error_category_c : public error_category { // categorize an operating system error
public:
system_error_category_c() : error_category() {}
virtual const char *name() const { return "system"; }
virtual std::string message(int ec) const
{
try {
return format_message(ec);
}
catch (...) {
return "unknown error";
}
}
};
namespace detail {
template<typename T> T& put_on_static_storage() {
static T storage;
return storage;
}
}
error_category& system_category() {
return detail::put_on_static_storage<system_error_category_c>();
}
using std::runtime_error;
class system_error : public runtime_error
{
public:
system_error(std::error_code ec, const std::string& m)
: ec_(ec), runtime_error((m.empty()) ? ec.message() : m + ": " + ec.message())
{}
system_error(std::error_code ec) : system_error(ec, "") {}
system_error(std::error_code ec, const char *m) : system_error(ec, std::string(m)) {}
system_error(int e, const error_category& erct) : system_error(e, erct, "") {}
system_error(int e, const error_category& erct, const std::string& m) : system_error(std::error_code(e, erct), m) {}
system_error(int e, const error_category& erct, const char *m) : system_error(e, erct, std::string(m)) {}
const std::error_code& code() const { return ec_; }
private:
std::error_code ec_;
};
#else
using std::system_category;
using std::system_error;
#endif
使用例