Привет,
Есть куча енумов в интерфейсе используемой библиотеки.
Интерфейс следующего слоя абстракции используют примерно эти же енумы, но под другими именами и слегка другими полями.
Требуются методы преобразования енумов каждого типа.
Так вот, не хочется создавать кучу функций вида LibraryStatus2ProgramStatus. Вместо этого хотелось бы чего-нибудь, типа перегрузки оператора присвоения или каста как функции — нечлена, аля
//жалко так нельзя
ePROGRAM_STATUS operator=(eLIBRARY_STATUS& from)
{
//тут тело функции LibraryStatus2ProgramStatus
}
//и потом в коде:
eLIBRARY_STATUS lst;
ePROGRAM_STATUS pst;
pst = lst;
//ну или хотябы:
ePROGRAM_STATUS operator ePROGRAM_STATUS(eLIBRARY_STATUS)
{
//тут тело функции LibraryStatus2ProgramStatus
}
//и потом в коде:
eLIBRARY_STATUS lst;
ePROGRAM_STATUS pst;
pst = (ePROGRAM_STATUS)lst; //чтобы тут не тупой каст инта в инт, а правильный.
А как это можно написать не по уродски?
хотел уже на боковую
папаху снял и сапоги
но в комментариях проснулись
враги
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Тебя интересует сам по себе вызов перекодирующей функции?
Ага. Не, ну если ты знаешь еще изящный способ перекодирования, без написания свитчей руками, я тоже не откажусь
E>1) Как много таких пар перечислений?
Туева хуча. Но большинство уже перекодируются прямо в коде, это я так, на будущее.
E>2) Как сложно писать сами перекодирующие функции?
Не понял вопроса. Ну, я же привел пример функции, как сложно её написать?
E>Например можно так:
Ага, спасибо. Я примерно это же и нарисовал.
template<typename T, typename U>
T my_enum_cast(U in )
{
//Will fire compiler error when this generic template is instantiated
BOOST_STATIC_ASSERT_MSG(!std::is_integral<U>::value, "No conversion defined for this enum types, please define one.");
return (T)in;
}
template<>
ePROGRAM_STATUS my_enum_cast(eLIBRARY_STATUS err)
{
return LibraryStatus2ProgramStatus(err);
}
//вызов
eLIBRARY_STATUS err;
eServiceErrorCode = my_enum_cast<ePROGRAM_STATUS>(err);
Тут, правда, пара моментов мучает:
1. А мой статик ассерт насколько портабелен? Если вместо этого извращения с !std::is_integral<U>::value подставить просто false, он всегда срабатывает, даже когда generic специализация не инстанциируется.
2. А может, разрешить таки c-style каст по умолчанию?
3. Как я понимаю, то, что я могу в точке вызова не писать <ePROGRAM_STATUS, eLIBRARY_STATUS >, а достаточно <ePROGRAM_STATUS> — это только с с++11, или в 03 тоже прокатит? И что мешает компилятору вывести и второй тип тоже, который возвращается? Чтобы сразу писать:
BT>template<typename T, typename U>
BT>T my_enum_cast(U in )
BT>{
BT> //Will fire compiler error when this generic template is instantiated
BT> BOOST_STATIC_ASSERT_MSG(!std::is_integral<U>::value, "No conversion defined for this enum types, please define one.");
BT> return (T)in;
BT>}
BT>template<>
BT>ePROGRAM_STATUS my_enum_cast<ePROGRAM_STATUS, eLIBRARY_STATUS>(eLIBRARY_STATUS err)
BT>{
BT> return LibraryStatus2ProgramStatus(err);
BT>}
BT>//вызов
BT>eLIBRARY_STATUS err;
BT>eServiceErrorCode = my_enum_cast<ePROGRAM_STATUS>(err);
BT>
BT>Тут, правда, пара моментов мучает: BT>1. А мой статик ассерт насколько портабелен? Если вместо этого извращения с !std::is_integral<U>::value подставить просто false, он всегда срабатывает, даже когда generic специализация не инстанциируется.
Можно шаблонную функцию вообще только объявить, но не определять... BT>2. А может, разрешить таки c-style каст по умолчанию?
Зачем? BT>3. Как я понимаю, то, что я могу в точке вызова не писать <ePROGRAM_STATUS, eLIBRARY_STATUS >, а достаточно <ePROGRAM_STATUS> — это только с с++11, или в 03 тоже прокатит? И что мешает компилятору вывести и второй тип тоже, который возвращается? Чтобы сразу писать:
Прокатит, а мешают правила вывода.
E>>Если не хочется в точках вызова писать везде ePROGRAM_STATUS, то можно ещё такой шаблон добавить: E>>
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Brice Tribbiani, Вы писали:
BT>Ага. Не, ну если ты знаешь еще изящный способ перекодирования, без написания свитчей руками, я тоже не откажусь
Ну зависит от того, как те перечисления заданы. Можно написать генерилку, например...
E>>1) Как много таких пар перечислений? BT>Туева хуча. Но большинство уже перекодируются прямо в коде, это я так, на будущее.
Ну, тут может быть лучше другая стратегия. Типа пишешь набор функций, перекодирующих между слоями.
Ну там семейство toProgramEnum для перевода из перечислений разных библ в перечисления программы, например.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
E>Можно шаблонную функцию вообще только объявить, но не определять...
Ошибка переползает на время линковки, это не совсем то, что хочется. Со статик ассертом сразу понятно, что к чему. Переписал его пока вот в таком виде:
BOOST_STATIC_ASSERT_MSG(!(std::is_enum<U>::value || !std::is_enum<U>::value) , "No conversion defined for this enum types, please define one.");
Но не знаю, может ли какой шибко умный компайлер соптимизировать условие сразу в false и ругнуться без инстанциации.
BT>>2. А может, разрешить таки c-style каст по умолчанию? E>Зачем?
Ну, если поля енумов совпадают, чтобы можно было не реализовывать каст
BT>>Не знаю, как-то не человеко-читаемо, по моему.
E>А так: E>
Let( res ) = STATUS_OK;
Ну, вот так, наверно, можно, если вместо let какое-нибудь говорящее имя придумать. Хотя пока что enum_cast<ePROGRAM_STATUS> кажется оптимальным балансом количества писанины/легкости понимания.
хотел уже на боковую
папаху снял и сапоги
но в комментариях проснулись
враги
но это несколько менее надёжно, чем вариант со switch, т.к. тут используется соответствие численных значений, а при некоторых изменениях определения какого-либо из этих перечислений такое соответствие может быть нарушено.
BT>Тут, правда, пара моментов мучает: BT>1. А мой статик ассерт насколько портабелен? Если вместо этого извращения с !std::is_integral<U>::value подставить просто false, он всегда срабатывает, даже когда generic специализация не инстанциируется.
Если из шаблона нельзя получить ни одну валидную специализацию, то программа ill-formed (в данном случае компилятор может, но не обязан это диагностировать).
Brice Tribbiani:
BT>Ошибка переползает на время линковки, это не совсем то, что хочется. Со статик ассертом сразу понятно, что к чему. Переписал его пока вот в таком виде: BT>
BT>BOOST_STATIC_ASSERT_MSG(!(std::is_enum<U>::value || !std::is_enum<U>::value) , "No conversion defined for this enum types, please define one.");
BT>
BT>Но не знаю, может ли какой шибко умный компайлер соптимизировать условие сразу в false и ругнуться без инстанциации.
Теоретически компилятор может вывести, что условие !(std::is_enum<U>::value || !std::is_enum<U>::value) никогда не выполняется и по данному шаблону ничего нельзя сгенерировать. Для надёжности можно было бы использовать какой-нибудь вспомогательный шаблон, который позволял бы сопоставлять произвольному типу некое фиксированное значение, но при этом компилятор не мог бы считать его фиксированным из-за потенциальной возможности добавлять частичные или явные специализации, где значение могло бы быть другим:
// DO NOT SPECIALIZE!template <class Dependency, class ValueType, ValueType Value>
struct dependent_value
{ static ValueType const value = Value; };
template <class Dependency, class ValueType, ValueType Value>
ValueType const dependent_value<Dependency, ValueType, Value>::value;
BOOST_STATIC_ASSERT_MSG(dependent_value<U, bool, false>::value , "No conversion is defined for these enum types.");
Здравствуйте, N. I., Вы писали:
NI>Brice Tribbiani:
E>>>2) Как сложно писать сами перекодирующие функции? BT>>Не понял вопроса. Ну, я же привел пример функции, как сложно её написать?
NI>Конкретно в данном случае можно было бы использовать static_cast
NI>но это несколько менее надёжно, чем вариант со switch, т.к. тут используется соответствие численных значений, а при некоторых изменениях определения какого-либо из этих перечислений такое соответствие может быть нарушено.
Ну, это понятно.
BT>>Тут, правда, пара моментов мучает: BT>>1. А мой статик ассерт насколько портабелен? Если вместо этого извращения с !std::is_integral<U>::value подставить просто false, он всегда срабатывает, даже когда generic специализация не инстанциируется.
NI>Если из шаблона нельзя получить ни одну валидную специализацию, то программа ill-formed (в данном случае компилятор может, но не обязан это диагностировать).
То есть, как я понял:
=delete, как я понимаю, с c++11. Но, если есть возможность использовать его — то надо его и использовать. Или к нему тоже применимо вот это "Если из шаблона нельзя получить ни одну валидную специализацию, то программа ill-formed"?
static_assert(false,"") — ill-formed, потому что "из шаблона нельзя получить ни одну валидную специализацию", и поэтому компилятор выдает его диагностику, несмотря на отсутствие инстанциации.
static_assert<dependent_value<U, bool, false>::value, ""> — тоже будет портабельно.
Правильно?
хотел уже на боковую
папаху снял и сапоги
но в комментариях проснулись
враги
Brice Tribbiani:
BT>То есть, как я понял:
BT>=delete, как я понимаю, с c++11.
Верно.
BT>Но, если есть возможность использовать его — то надо его и использовать.
Ну, скажем так, это использование =delete по его прямому назначению.
BT>Или к нему тоже применимо вот это "Если из шаблона нельзя получить ни одну валидную специализацию, то программа ill-formed"?
Определение функции как deleted не делает её невалидной, это делает невалидным только использование её некоторыми способами:
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
BT>static_assert(false,"") — ill-formed, потому что "из шаблона нельзя получить ни одну валидную специализацию", и поэтому компилятор выдает его диагностику, несмотря на отсутствие инстанциации.
Верно. Правило такое:
If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required.
Под valid неявно подразумевается well-formed. Использование static_assert(false,"") делает программу ill-formed:
If the value of the expression when so converted is true, the declaration has no effect. Otherwise, the program is ill-formed, and the resulting diagnostic message (1.4) shall include the text of the string-literal, except that characters not in the basic source character set (2.3) are not required to appear in the diagnostic message.
BT>static_assert<dependent_value<U, bool, false>::value, ""> — тоже будет портабельно.
BT>Правильно?