На самом деле вы можете использовать его обоими способами, то есть иметь две функции, которые переводят код в ошибку и обратно.
Прежде всего, конечно, #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_); \
}
Мы могли бы быть немного более жестокими в случае регистрации неизвестной ошибки (утверждают, например).
И затем мы всегда можем обернуть этот макрос в один, который может определить несколько символов за один раз ... но это осталось в качестве упражнения.
Использование:
// 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;
}
}
Отказ от ответственности: Iя не слишком уверен насчет лямбда-синтаксиса, но он упростил написание.