実際には両方の方法で使用できます。つまり、コードからエラーに変換する 2 つの関数を使用できます。
もちろん、最初のことは#define
、定数には使用しないでください。おそらく列挙型が最適ですが、列挙型は拡張できません。これには、すべてのエラーを同じ場所で定義する必要があります (依存関係についてどうもありがとう. ..)
ただし、名前空間を使用してシンボルを分離し、前処理して世代全体を処理することで、別の方法で実行できます。ルックアップ部分では、Bimapを使用します。
最初に Handler クラスを定義する必要があります (すべてをインライン化する必要はありませんが、例としては簡単です)
#include <boost/bimap.hpp>
#include <boost/optional.hpp>
namespace error
{
class Handler
{
public:
typedef boost::optional<int> return_code;
typedef boost::optional<std::string> return_description;
static bool Register(int code, const char* description)
{
typedef error_map::value_type value_type;
bool result = MMap().insert(value_type(code,description)).second;
// assert(result && description)
return result;
}
static return_code GetCode(std::string const& desc)
{
error_map::map_by<description>::const_iterator it
= MMap().by<description>().find(desc);
if (it != MMap().by<description>().end()) return it->second;
else return return_code();
}
static return_description GetDescription(int c)
{
error_map::map_by<code>::const_iterator it
= MMap().by<code>().find(c);
if (it != MMap().by<code>().end()) return it->second;
else return return_description();
}
typedef std::vector< std::pair<int,std::string> > errors_t;
static errors_t GetAll()
{
errors_t result;
std::for_each(MMap().left.begin(), MMap().left.end(),
result.push_back(boost::lambda::_1));
return result;
}
private:
struct code {};
struct description {};
typedef boost::bimap<
boost::tagged<int, code>,
boost::tagged<std::string, description>
> error_map;
static error_map& Map() { static error_map MMap; return MMap; }
};
// Short-Hand
boost::optional<int> GetCode(std::string const& d)
{
return Handler::GetCode(d);
}
boost::optional<std::string> GetDescription(int c)
{
return Handler::GetDescription(c);
}
} // namespace error
次に、シンタックス シュガーを提供する必要があります。
#define DEFINE_NEW_ERROR(Code_, Description_) \
const int Description_ = Code_; \
namespace error { \
const bool Description##_Registered = \
::error::Handler::Register(Code_, #Description_); \
}
不明なエラー (たとえば assert) が登録された場合は、もう少し暴力的になる可能性があります。
そして、そのマクロを一度に複数のシンボルを定義できるマクロにいつでもラップできます... しかし、それは演習として残します。
使用法:
// someErrors.hpp
#include "error/handler.hpp"
DEFINE_NEW_ERROR(1, AnError)
DEFINE_NEW_ERROR(2, AnotherError)
// someFile.cpp
#include <iostream>
#include "error/handler.hpp"
int main(int argc, char* argv[])
{
int code = 6;
boost::optional<std::string> desc = error::GetDescription(code);
if (desc)
{
std::cout << "Code " << code << " is mapped to <" << *desc << ">" << std::endl;
}
else
{
std::cout << "Code " << code << " is unknown, here is the list:\n";
::error::Handler::errors_t errors = ::Error::Handler::GetAll();
std::for_each(errors.begin(), errors.end(), std::cout << " " << _1);
std::cout << std::endl;
}
}
免責事項:ラムダ構文についてはよくわかりませんが、記述が簡素化されました。