Я имел дело с этой ситуацией ранее, для кодов ошибок.
Я видел людей, использующих перечисления для кодов ошибок, и это создает некоторые проблемы:
- , которые вы можете назначитьint для перечисления, которое не соответствует ни одному значению (слишком плохо)
- само значение объявлено в заголовке, что означает, что переназначение кода ошибки (это происходит ...) нарушает совместимость кода, вытакже нужно соблюдать осторожность при добавлении элементов ...
- Вы должны определить все коды в одном и том же заголовке, даже если часто какой-то код естественным образом ограничивается небольшой частью приложения, потому что перечисления не могут быть "расширенный "
- нет проверки того, что один и тот же код не назначен дважды
- вы не можете перебирать различные поля
enum
При разработке моегоРешение с помощью кодов ошибок, таким образом, я выбрал другой путь: константы в пространстве имен, определенные в исходных файлах, которые обращаются к пунктам 2 и 3. Хотя для обеспечения безопасности типов, константы не int
, аопределенный Code
класс:
namespace error { class Code; }
Тогда я могу определить несколько файлов ошибок:
// error/common.hpp
namespace error
{
extern Code const Unknown;
extern Code const LostDatabaseConnection;
extern Code const LostNASConnection;
}
// error/service1.hpp
// error/service2.hpp
Я не решил произвольную проблему приведения (конструктор явный, но общедоступный)потому что в моем случае от меня требовалось пересылать коды ошибок, возвращенные другими серверами, и я, конечно, не хотел знать их все (это было бы слишком хрупко)
Однако я думал об этомсделав требуемый конструктор частным и принудительно применяя конструктор, мы даже сразу получим 4. и 5.:
// error/code.hpp
namespace error
{
class Code;
template <size_t constant> Code const& Make(); // not defined here
class Code: boost::totally_ordered<Code>
{
public:
Code(): m(0) {} // Default Construction is useful, 0 is therefore invalid
bool operator<(Code const& rhs) const { return m < rhs.m; }
bool operator==(Code const& rhs) const { return m == rhs.m; }
private:
template <size_t> friend Code const& Make();
explicit Code(size_t c): m(c) { assert(c && "Code - 0 means invalid"); }
size_t m;
};
std::set<Code> const& Codes();
}
// error/privateheader.hpp (inaccessible to clients)
namespace error
{
std::set<Code>& PrivateCodes() { static std::set<Code> Set; return Set; }
std::set<Code> const& Codes() { return PrivateCodes(); }
template <size_t constant>
Code const& Make()
{
static std::pair< std::set<Code>::iterator, bool > r
= PrivateCodes().insert(Code(constant));
assert(r.second && "Make - same code redeclared");
return *(r.first);
}
}
//
// We use a macro trick to create a function whose name depends
// on the code therefore, if the same value is assigned twice, the
// linker should complain about two functions having the same name
// at the condition that both are located into the same namespace
//
#define MAKE_NEW_ERROR_CODE(name, value) \
Make<value>(); void _make_new_code_##value ();
// error/common.cpp
#include "error/common.hpp"
#include "privateheader.hpp"
namespace error
{
Code const Unkown = MAKE_NEW_ERROR_CODE(1)
/// ....
}
чуть больше работы (для фреймворка),и только проверка времени соединения / выполнения одной и той же проверки назначений.Хотя дубликаты легко диагностировать, просто отсканировав шаблон MAKE_NEW_ERROR_CODE
Веселитесь!