Здравствуйте, BSOD, Вы писали:
A>>Как это записать в современном C++, чтобы не было performance penalty? Без конструирования контейнера и т.п. Нормальных макросов же (как в Немерле), насколько я понимаю, не завезли?
BSO>
BSO>switch(errno)
BSO> case EAGAIN:
BSO> case EWOULDBLOCK:
BSO> case EINTR:
BSO> case ENOSPC:
BSO> case ENOBUFS:
BSO> case ENOMEM:
BSO> {...}
BSO>
Выше написали, что при дублировании будет ошибка компиляции. А от себя добавлю, что ! выражать через default это верный способ сделать больше ошибок, а не меньше.
Здравствуйте, Dair, Вы писали:
D>Здравствуйте, Alekzander, Вы писали:
A>> if (!(errno == EAGAIN || EWOULDBLOCK || A>> errno == EINTR || errno == ENOSPC || A>> errno == ENOBUFS || errno == ENOMEM)) {
D>Коллеги выше красиво написали, но почему-то упустили тот факт, что EWOULDBLOCK не находится в списке значений, которые не должен принимать errno.
Этот фрагмент, как упоминается в исходной статье от студии PVS, содержит ошибку.
В исправленной версии там именно сравнение с errno.
Вот бы уменьшить число сравнений с помощью бинарного поиска в упорядоченном ряде значений.
Взять какой-нибудь constexpr set и вызвать метод contains().
Здравствуйте, BSOD, Вы писали:
BSO>switch(errno) BSO> case EAGAIN: BSO> case EWOULDBLOCK: BSO> case EINTR: BSO> case ENOSPC: BSO> case ENOBUFS: BSO> case ENOMEM: BSO> {...}
Тут есть одна проблемка: на большинстве систем коды EAGAIN и EWOULDBLOCK сейчас совпадают (такое вот легаси), но есть специфические, где они различны. При совпадении switch выдаёт ошибку.
Можно проверять препроцессором, но это ещё больше левых слов.
А в варианте с == проблемы не возникает
Здравствуйте, Sm0ke, Вы писали:
S>Вот бы уменьшить число сравнений с помощью бинарного поиска в упорядоченном ряде значений. S>Взять какой-нибудь constexpr set и вызвать метод contains().
C++ и так это способен оптимизировать до битовых полей, что бы сравнивать группами.
Здравствуйте, Alekzander, Вы писали:
A>Код надо писать так, чтобы его нельзя было записать неправильно
Такой подход неизбежно вырождается в известное "создайте систему, которой сможет пользоваться дурак, и только дурак захочет ею пользоваться". Невозможно (и не нужно) загодя предусмотреть все мыслимые косяки, которые могут возникнуть в применении какого-либо инструмента. А вот возможность доработки инструмента предусматривать как раз полезно — ту же болгарку можно использовать и с защитным кожухом, и без него, или, наоборот, зажать в какой-нибудь привод и накрыть всю эту конструкцию общим кожухом.
В данном случае самое правильное — с одной стороны, иметь в языке средства для удобной и эффективной записи подобных конструкций, а с другой — иметь в его компиляторе предупреждения на любые подозрительные конструкции.
Здравствуйте, Евгений Музыченко, Вы писали:
A>>Код надо писать так, чтобы его нельзя было записать неправильно
ЕМ>Такой подход неизбежно вырождается в известное "создайте систему, которой сможет пользоваться дурак, и только дурак захочет ею пользоваться". Невозможно (и не нужно) загодя предусмотреть все мыслимые косяки, которые могут возникнуть в применении какого-либо инструмента. А вот возможность доработки инструмента предусматривать как раз полезно — ту же болгарку можно использовать и с защитным кожухом, и без него, или, наоборот, зажать в какой-нибудь привод и накрыть всю эту конструкцию общим кожухом.
ЕМ>В данном случае самое правильное — с одной стороны, иметь в языке средства для удобной и эффективной записи подобных конструкций, а с другой — иметь в его компиляторе предупреждения на любые подозрительные конструкции.
Я понял. На самом деле я дурак, и мне это не надо. Классика русскоязычных форумов по программированию ))
Здравствуйте, Alekzander, Вы писали:
A>На самом деле я дурак, и мне это не надо. Классика русскоязычных форумов по программированию ))
Возможно, Вам "это" и надо, но, получив частное решение, Вы скоро столкнетесь с похожим случаем, "вот точно таким же, только другим", и для него снова потребуется частное решение. На какой итерации уже надоест, и захочется более общего?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Возможно, Вам "это" и надо, но, получив частное решение, Вы скоро столкнетесь с похожим случаем, "вот точно таким же, только другим", и для него снова потребуется частное решение. На какой итерации уже надоест, и захочется более общего?
Здравствуйте, Евгений Музыченко, Вы писали:
A>>На самом деле я дурак, и мне это не надо. Классика русскоязычных форумов по программированию ))
ЕМ>Возможно, Вам "это" и надо, но, получив частное решение, Вы скоро столкнетесь с похожим случаем, "вот точно таким же, только другим", и для него снова потребуется частное решение. На какой итерации уже надоест, и захочется более общего?
Я внезапно понял, что ты просто не умеешь программировать. Зато умничаешь как Александреску.
Любой, буквально любой джун, если он хотя бы год попрограммировал, увидит тут следующее. У нас есть одна и та же операция над каждым элементом множества. Но мы записываем её не один раз, а копируем код операции для каждого элемента. Это называется "нарушение DRY" в виде копипасты. К чему приводит копипаста очень хорошо видно из статьи, на которую я сослался. Но есть и другие соображения. Например, если у нас сравнение нетривиальное, и цена его после рефакторинга выросла, в is_in_set простое развёртывание можно заменить кешированием профиля. Но это обсуждение, боюсь, требует другого уровня квалификации. Такого, когда человек сам способен разглядеть копипасту, а не ждёт, когда его натычут в неё носом.
В том, что устраняется лишь один вид возможной опечатки, и реализуется так же лишь один вид сравнений (простая однородная последовательность). Во менее однородных сравнениях (например, где нужно дополнительно проверить другие условия) все останется по-прежнему.
Это не говоря уже о том, что сделать опечатку в вызове такого хитровыгнутого шаблона тоже достаточно легко, и по кучке сообщений об ошибках, которую компилятор насыпет в ответ, тоже далеко не сразу поймешь, что именно не так.
? ЕМ>В том, что устраняется лишь один вид возможной опечатки, и реализуется так же лишь один вид сравнений (простая однородная последовательность). Во менее однородных сравнениях (например, где нужно дополнительно проверить другие условия) все останется по-прежнему.
Видите ли, это у автора темы был вопрос про опечатку, я же писал этот класс из совсем других соображений, главным образом для компактности и выразительности кода. Обычным использованием этого класса у меня является такой код: assert((The(fileType).one_of<0, 1, 2, 3, 4>()));
Мне легче его читать, чем пять сравнений соединённых через ||
Реже встречаются строчки вида
if ( The(m_phase).one_of<EPhase::eIdle, EPhase::ePauseBetweenSeries>() )
Для меня этот код не частный, а наоборот, слишком общий и некоторые его возможности вообще не используются. Например, вот такое его применение я не нахожу оправданным
int a = 3;
int b = 4;
int c = 5;
int d = 5;
std::cout << The(true).one_of(a == b, b == c, c == a) << std::endl;
std::cout << The(true).one_of(a == b, b == c, c == d) << std::endl;
Поэтому иногда, чтобы избежать общности, я заменяю его более частными случаями прописывая метод класса вида:
if ( IsMode<Mode::A>() )
...
if ( IsMode<Mode::A, Mode::B>() )
...
Короче, я хочу сказать, что нахожу такой код чересчур общим, а никак не частным.
ЕМ>Это не говоря уже о том, что сделать опечатку в вызове такого хитровыгнутого шаблона тоже достаточно легко, и по кучке сообщений об ошибках, которую компилятор насыпет в ответ, тоже далеко не сразу поймешь, что именно не так.
Лучше получить ошибку компиляции, чем ошибку выполнения.
Здравствуйте, B0FEE664, Вы писали:
BFE>assert((The(fileType).one_of<0, 1, 2, 3, 4>())); BFE>Мне легче его читать, чем пять сравнений соединённых через ||
И Вас не смущает, что из такой формулировки, как правило, не следует, какой смысл имеет данное подмножество и, по-хорошему, нужен дополнительный комментарий?
BFE>нахожу такой код чересчур общим, а никак не частным.
Я тоже очень люблю всяческие избыточные проверки и assert'ы, но не припомню, чтоб мне приходилось в товарных количествах проверять переменные на равенство не связанным между собой значениям. Разовые проверки обычно укладываются максимум в два-три варианта или проверку вхождения в диапазон. Если же набор вариантов более сложный, как в примере у ТС, и встречается более одного раза, то гораздо правильнее сделать осмысленный предикат "значение удовлетворяет условию".
А если делать средства, как у Вас, просто для компактной и "более надежной" записи систематических выражений, можно докатиться и до замены "a + b + c" на "sum (a, b, c)".
В принципе, я вовсе не против подобных средств группировки. Но засада-то в том, что это сильно избыточно (на каждый вызов создается отдельный класс, который нигде больше не нужен), и весьма уродливо (реализация пакетов параметров шаблона, как очередное сугубо частное решение, худо-бедно годится для подобных последовательностей, а последовательности более другого вида не поймет, и их по-прежнему придется выписывать руками).
Здравствуйте, Alekzander, Вы писали:
A>Тут недавно была статья от создателей PVS, как надо и не надо писать код. В ней автор приводил следующий пример с ошибкой:
A>
A>void adns__querysend_tcp(adns_query qu, struct timeval now) {
A> ...
A> if (!(errno == EAGAIN || EWOULDBLOCK ||
A> errno == EINTR || errno == ENOSPC ||
A> errno == ENOBUFS || errno == ENOMEM)) {
A> ...
A>}
A>
Я б вот так написал:
if (errno == EAGAIN) {
errno = EWOULDBLOCK;
}
switch (errno) {
case EWOULDBLOCK:
case EINTR:
case ENOSPC:
case ENOBUFS:
case ENOMEM:
. . . .
}
С EWOULDBLOCK есть та проблема, что на некоторых системах это alias к EAGAIN, а на некоторых — нет. Поэтому с ним приходится что-то некрасивое делать.
Здравствуйте, Alekzander, Вы писали:
A>Предикат через switch-case... такое. Тем более, в данном случае по невнимательности ошибок ещё больше настряпаешь, чем с простым if'ом.
Предикат черес switch-case хорош тем, что в него очень легко и естественно добавлять упущенные значения.