Сообщение Re: ADL не ADL? Или как? от 07.01.2024 20:42
Изменено 07.01.2024 20:55 rg45
Re: ADL не ADL? Или как?
Здравствуйте, Marty, Вы писали:
M>Есть у меня сериализация/десериализация enum'ов.
M>Она на внешнем генераторе, в результате его работы в том же пространстве имён, в котором объявлен enum, появляется функция:
M>
M>Обычно у меня есть EnumType::invalidValue, я его передаю как defVal, и если на выходе получил EnumType::invalidValue, то кидаю исключение.
M>Поиск реализации идёт через ADL по дефолтному значению, и всё в общем-то работает.
M>Есть ещё функция
M>
M>Она сама умеет кидать исключение, но её имя генерится на макросах и зависит от имени enum'а.
M>Теперь я хочу сделать шаблонную функцию enum_deserialize, чтобы тип enum'а она получала как параметр шаблона.
M>Пока я сделал, основываясь на том, что EnumType::invalidValue у меня всегда -1, и я использую первую версию, передаю туда инвалида (-1), и на выходе с этим инвалидом сравниваю, и кидаю исключение, если что-то пошло не так.
M>Но это завязано на мои соглашения, что EnumType::invalidValue у меня всегда -1. С enum'ами, константы для которых могут зависеть от внешних спек, такое может не прокатить.
M>Наивная идея была сделать общий шаблон enum_deserialize_throwable, и генерить специализации для него. Но потом я вспомнил, что все сгенерённые функции попадают в то же пространство имён, что и сам enum, и это могут быть любые NS, и точно не то NS, в котором объявлен шаблон, который я хотел специализировать.
M>Как быть?
Честно признаюсь, не уверен, что полностью правильно понял все сценарии, но попробую предложить решение, а вдруг попаду. Ты можешь для каждого энума определить функцию получения дефолтного значения. Разумеется, эта функция должна быть определена в том же пространстве имен, что и сам энум — чтобы она могла быть найденной через ADL. Один лишь нюанс: для того, чтобы ADL правильно работал, у этой функции должен быть один формальный параметр, имеющий тип этого энума. При вызове этих функций можно будет передавать фактический параметр, сконструированный по дефолту. Еще один замечательный момент: эти функции, получающие дефолтный параметр могут быть как шаблонными, так и обычными, нешаблонными. Схематично так:
Ну и потом где-то дальше использование в произвольном пространстве имен:
Можно еще заморочиться и создать универсальную шаблонную обертку вообще для всех-всех энумов, которая будет создавать дефолтный экземпляр энума нужного типа и передавать его в "рабочу лошадку" — акксесор:
[cpp]
namespace ns2
{
template <typename T>
T 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
Но это уже для совсем уж больших любителей синтаксического сахара, без этого вполне можно жить, я считаю.
M>Есть у меня сериализация/десериализация enum'ов.
M>Она на внешнем генераторе, в результате его работы в том же пространстве имён, в котором объявлен enum, появляется функция:
M>
M>inline
M>EnumType enum_deserialize(const std::string &str, EnumType defVal);
M>
M>Обычно у меня есть EnumType::invalidValue, я его передаю как defVal, и если на выходе получил EnumType::invalidValue, то кидаю исключение.
M>Поиск реализации идёт через ADL по дефолтному значению, и всё в общем-то работает.
M>Есть ещё функция
M>
M>inline
M>EnumType enum_deserialize_##EnumType(const std::string &str);
M>
M>Она сама умеет кидать исключение, но её имя генерится на макросах и зависит от имени enum'а.
M>Теперь я хочу сделать шаблонную функцию enum_deserialize, чтобы тип enum'а она получала как параметр шаблона.
M>Пока я сделал, основываясь на том, что EnumType::invalidValue у меня всегда -1, и я использую первую версию, передаю туда инвалида (-1), и на выходе с этим инвалидом сравниваю, и кидаю исключение, если что-то пошло не так.
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
Можно еще заморочиться и создать универсальную шаблонную обертку вообще для всех-всех энумов, которая будет создавать дефолтный экземпляр энума нужного типа и передавать его в "рабочу лошадку" — акксесор:
[cpp]
namespace ns2
{
template <typename T>
T 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
Но это уже для совсем уж больших любителей синтаксического сахара, без этого вполне можно жить, я считаю.
Re: ADL не ADL? Или как?
Здравствуйте, Marty, Вы писали:
M>Есть у меня сериализация/десериализация enum'ов.
M>Она на внешнем генераторе, в результате его работы в том же пространстве имён, в котором объявлен enum, появляется функция:
M>
M>Обычно у меня есть EnumType::invalidValue, я его передаю как defVal, и если на выходе получил EnumType::invalidValue, то кидаю исключение.
M>Поиск реализации идёт через ADL по дефолтному значению, и всё в общем-то работает.
M>Есть ещё функция
M>
M>Она сама умеет кидать исключение, но её имя генерится на макросах и зависит от имени enum'а.
M>Теперь я хочу сделать шаблонную функцию enum_deserialize, чтобы тип enum'а она получала как параметр шаблона.
M>Пока я сделал, основываясь на том, что EnumType::invalidValue у меня всегда -1, и я использую первую версию, передаю туда инвалида (-1), и на выходе с этим инвалидом сравниваю, и кидаю исключение, если что-то пошло не так.
M>Но это завязано на мои соглашения, что EnumType::invalidValue у меня всегда -1. С enum'ами, константы для которых могут зависеть от внешних спек, такое может не прокатить.
M>Наивная идея была сделать общий шаблон enum_deserialize_throwable, и генерить специализации для него. Но потом я вспомнил, что все сгенерённые функции попадают в то же пространство имён, что и сам enum, и это могут быть любые NS, и точно не то NS, в котором объявлен шаблон, который я хотел специализировать.
M>Как быть?
Честно признаюсь, не уверен, что полностью правильно понял все сценарии, но попробую предложить решение, а вдруг попаду. Ты можешь для каждого энума определить функцию получения дефолтного значения. Разумеется, эта функция должна быть определена в том же пространстве имен, что и сам энум — чтобы она могла быть найденной через ADL. Один лишь нюанс: для того, чтобы ADL правильно работал, у этой функции должен быть один формальный параметр, имеющий тип этого энума. При вызове этих функций можно будет передавать фактический параметр, сконструированный по дефолту. Еще один замечательный момент: эти функции, получающие дефолтный параметр могут быть как шаблонными, так и обычными, нешаблонными. Схематично так:
Ну и потом где-то дальше использование в произвольном пространстве имен:
Можно еще заморочиться и создать универсальную шаблонную обертку вообще для всех-всех энумов, которая будет создавать дефолтный экземпляр энума нужного типа и передавать его в "рабочу лошадку" — акксесор:
Но это уже для совсем уж больших любителей синтаксического сахара, без этого вполне можно жить, я считаю.
M>Есть у меня сериализация/десериализация enum'ов.
M>Она на внешнем генераторе, в результате его работы в том же пространстве имён, в котором объявлен enum, появляется функция:
M>
M>inline
M>EnumType enum_deserialize(const std::string &str, EnumType defVal);
M>
M>Обычно у меня есть EnumType::invalidValue, я его передаю как defVal, и если на выходе получил EnumType::invalidValue, то кидаю исключение.
M>Поиск реализации идёт через ADL по дефолтному значению, и всё в общем-то работает.
M>Есть ещё функция
M>
M>inline
M>EnumType enum_deserialize_##EnumType(const std::string &str);
M>
M>Она сама умеет кидать исключение, но её имя генерится на макросах и зависит от имени enum'а.
M>Теперь я хочу сделать шаблонную функцию enum_deserialize, чтобы тип enum'а она получала как параметр шаблона.
M>Пока я сделал, основываясь на том, что EnumType::invalidValue у меня всегда -1, и я использую первую версию, передаю туда инвалида (-1), и на выходе с этим инвалидом сравниваю, и кидаю исключение, если что-то пошло не так.
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>
T 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
Но это уже для совсем уж больших любителей синтаксического сахара, без этого вполне можно жить, я считаю.