Здравствуйте, Marty, Вы писали:
M>Но это завязано на мои соглашения, что EnumType::invalidValue у меня всегда -1. С enum'ами, константы для которых могут зависеть от внешних спек, такое может не прокатить.
M>Наивная идея была сделать общий шаблон enum_deserialize_throwable, и генерить специализации для него. Но потом я вспомнил, что все сгенерённые функции попадают в то же пространство имён, что и сам enum, и это могут быть любые NS, и точно не то NS, в котором объявлен шаблон, который я хотел специализировать.
M>Как быть?
Честно признаюсь, не уверен, что полностью правильно понял все сценарии, но попробую предложить решение, а вдруг попаду. Ты можешь для каждого энума определить функцию получения дефолтного значения. Разумеется, эта функция должна быть определена в том же пространстве имен, что и сам энум — чтобы она могла быть найденной через ADL. Один лишь нюанс: для того, чтобы ADL правильно работал, у этой функции должен быть один формальный параметр, имеющий тип этого энума. При вызове этих функций можно будет передавать фактический параметр, сконструированный по дефолту. Еще один замечательный момент: эти функции, получающие дефолтный параметр могут быть как шаблонными, так и обычными, нешаблонными. Схематично так:
namespace ns1
{
enum E1
{
invalidValue = -1,
. . .
};
enum E2
{
invalidValue = -1,
. . .
};
enum E3
{
None = -42,
. . .
};
enum E4
{
// No invalid value defined
Good1 = 1,
Good2 = 2,
Good3 = 3
};
// Comon accessor
template <typename T>
constexpr T enum_default_value(T) { return T::invalidValue; }
// Special accessor for E3
constexpr E3 enum_default_value(E3) { return E3::None; }
// Special accessor for E4
constexpr E4 enum_default_value(E4) { return {}; } // Returns zero-initialized E4 object
} // namespace ns1
Ну и потом где-то дальше ты сможешь единообразно получать дефолтные значения для разных типов энумов:
namespace ns2
{
void usage_example()
{
enum_default_value(ns1::E1{}); // returns E1::invalidValue
enum_default_value(ns1::E2{}); // returns E2::invalidValue
enum_default_value(ns1::E3{}); // returns E3::None
enum_default_value(ns1::E4{}); // returns E4{}
}
} // namespace ns2
Можно еще заморочиться и создать универсальную шаблонную обертку вообще для всех-всех типов энумов, которая будет создавать дефолтный экземпляр энума нужного типа и передавать его в "рабочу лошадку" — акксесор:
namespace ns2
{
template <typename T>
constexpr enum_default_value() { return enum_default_value(T{}); }
void usage_example()
{
enum_default_value<ns1::E1>(); // returns E1::invalidValue
enum_default_value<ns1::E2>(); // returns E2::invalidValue
enum_default_value<ns1::E3>(); // returns E3::None
enum_default_value<ns1::E4>(); // returns E4{}
}
} // namespace ns2
Но это уже для совсем уж больших любителей синтаксического сахара, без этого вполне можно жить, я считаю. И надо понимать, что эта обертка не сможет находиться по ADL, в отличие от реальных аксессоров, определенных в пространствах имен энумов, и объявление этой обертки всегда должно быть видно в точке ее использования.