Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 17.06.23 03:27
Оценка: +1
Тут недавно была статья от создателей PVS, как надо и не надо писать код. В ней автор приводил следующий пример с ошибкой:

void adns__querysend_tcp(adns_query qu, struct timeval now) {
  ...
  if (!(errno == EAGAIN || EWOULDBLOCK || 
        errno == EINTR || errno == ENOSPC ||
        errno == ENOBUFS || errno == ENOMEM)) {
  ...
}


А чтобы, значит, таких ошибок не допускать, он предлагал форматирование.

if (!(   errno == EAGAIN
      || EWOULDBLOCK
      || errno == EINTR
      || errno == ENOSPC
      || errno == ENOBUFS
      || errno == ENOMEM)) {


С моей точки зрения это глупость. Код надо писать так, чтобы его нельзя было записать неправильно хоть в строчку, хоть столбиком, табличкой, крестиком, козликом и т.д. Соответственно, единственный правильный вариант это вот такой вот псевдокод:

if (!(errno is in (
                       EAGAIN,
                       EWOULDBLOCK,
                       EINTR,
                       ENOSPC,
                       ENOBUFS,
                       ENOMEM
                   ))
{
...
}


Как это записать в современном C++, чтобы не было performance penalty? Без конструирования контейнера и т.п. Нормальных макросов же (как в Немерле), насколько я понимаю, не завезли?
Re: Как записать такое в современном C++?
От: vopl Россия  
Дата: 17.06.23 05:31
Оценка: 140 (10) +6
A>А чтобы, значит, таких ошибок не допускать, он предлагал форматирование.

A>
A>if (!(   errno == EAGAIN
A>      || EWOULDBLOCK
A>      || errno == EINTR
A>      || errno == ENOSPC
A>      || errno == ENOBUFS
A>      || errno == ENOMEM)) {
A>


A>С моей точки зрения это глупость.


да нормальный это подход, ничего в нем глупого нет. Табличное форматирование хорошо помогает видеть нарушения структурности, если его использовать побольше

A>Код надо писать так, чтобы его нельзя было записать неправильно хоть в строчку, хоть столбиком, табличкой, крестиком, козликом и т.д. Соответственно, единственный правильный вариант это вот такой вот псевдокод:


A>
A>if (!(errno is in (
A>                       EAGAIN,
A>                       EWOULDBLOCK,
A>                       EINTR,
A>                       ENOSPC,
A>                       ENOBUFS,
A>                       ENOMEM
A>                   ))
A>{
A>...
A>}
A>


A>Как это записать в современном C++, чтобы не было performance penalty? Без конструирования контейнера и т.п. Нормальных макросов же (как в Немерле), насколько я понимаю, не завезли?


например

#include <errno.h>

template <auto... set>
bool isin(auto val)
{
    return (false || ... || (set == val));
}

int main() 
{
    // 
    if(isin<EAGAIN,
            EWOULDBLOCK,
            EINTR,
            ENOSPC,
            ENOBUFS,
            ENOMEM>(errno))
    {
        return 0;
    }

    return 1;
}
Re: Как записать такое в современном C++?
От: fk0 Россия https://fk0.name
Дата: 17.06.23 07:24
Оценка: +1
Здравствуйте, 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>


switch-case. В голом C даже.
Re[2]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 17.06.23 07:58
Оценка:
Здравствуйте, vopl, Вы писали:

V>например


V>
V>#include <errno.h>

V>template <auto... set>
V>bool isin(auto val)
V>{
V>    return (false || ... || (set == val));
V>}

V>int main() 
V>{
V>    // 
V>    if(isin<EAGAIN,
V>            EWOULDBLOCK,
V>            EINTR,
V>            ENOSPC,
V>            ENOBUFS,
V>            ENOMEM>(errno))
V>    {
V>        return 0;
V>    }

V>    return 1;
V>}
V>


Спасибо, сразу вопросы. Лишний call выкинут все компиляторы? Есть стандартная реализация? Доколе шаблоны вместо макросов?
Re[2]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 17.06.23 08:01
Оценка: +1
Здравствуйте, fk0, Вы писали:

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>>


fk0> switch-case. В голом C даже.


Предикат через switch-case... такое. Тем более, в данном случае по невнимательности ошибок ещё больше настряпаешь, чем с простым if'ом.
Re[2]: Как записать такое в современном C++?
От: serg_joker Украина  
Дата: 17.06.23 08:21
Оценка: +3
Здравствуйте, vopl, Вы писали:

Унарной свёртки достаточно:
return ( ... || (set == val));
Re[3]: Как записать такое в современном C++?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.06.23 08:36
Оценка:
Здравствуйте, Alekzander, Вы писали:

fk0>> switch-case. В голом C даже.


A>Предикат через switch-case... такое. Тем более, в данном случае по невнимательности ошибок ещё больше настряпаешь, чем с простым if'ом.


inline-функция или лямбда (без захвата контекста), соптимизируется с ходу.
The God is real, unless declared integer.
Re[2]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 17.06.23 09:21
Оценка: 81 (2) +1
Здравствуйте, fk0, Вы писали:

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>>


fk0> switch-case. В голом C даже.


Если написать в лоб:
void adns__querysend_tcp(adns_query qu, struct timeval now) {
  auto is_err = false;
  switch(errno) {
    case EAGAIN:
    case EWOULDBLOCK:
    case EINTR:
    case ENOSPC:
    case ENOBUFS:
    case ENOMEM:
      is_err =  true;
  }
  if (!is_err) {

то возникнет ошибка компиляции на платформах, где EAGAIN == EWOULDBLOCK. Тогда как на платформах, где это разные значения, все будет нормально.

При этом тупое сравнение (errno == EAGAIN || errno == EWOULDBLOCK || ...) будет нормально компилироваться всегда.
Re[3]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 17.06.23 09:29
Оценка: 2 (1)
Здравствуйте, serg_joker, Вы писали:

_>Здравствуйте, vopl, Вы писали:


_>Унарной свёртки достаточно:

_>
_>return ( ... || (set == val));
_>


Интересно, а какое тогда значение будет возвращено при вот таком вызове: isin<>(errno) и почему?
Re[4]: Как записать такое в современном C++?
От: vopl Россия  
Дата: 17.06.23 09:36
Оценка: 10 (1) +2
Здравствуйте, so5team, Вы писали:

S>Здравствуйте, serg_joker, Вы писали:


_>>Здравствуйте, vopl, Вы писали:


_>>Унарной свёртки достаточно:

_>>
_>>return ( ... || (set == val));
_>>


S>Интересно, а какое тогда значение будет возвращено при вот таком вызове: isin<>(errno) и почему?


https://timsong-cpp.github.io/cppwp/temp.variadic#tab:temp.fold.empty
Re[5]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 17.06.23 09:39
Оценка: +2
Здравствуйте, vopl, Вы писали:

S>>Интересно, а какое тогда значение будет возвращено при вот таком вызове: isin<>(errno) и почему?


V>https://timsong-cpp.github.io/cppwp/temp.variadic#tab:temp.fold.empty


Большое спасибо!

Для себя сделаю вывод, что если стандарт не вызубрен наизусть, то безопаснее все-таки написать (false || ... || (set == val))
Re[3]: Как записать такое в современном C++?
От: serg_joker Украина  
Дата: 17.06.23 10:09
Оценка: 10 (4)
Здравствуйте, Alekzander, Вы писали:


A>Спасибо, сразу вопросы. Лишний call выкинут все компиляторы?

Я бы посмотрел на те компиляторы, которые интересны. Крайне рекомендую Compiler explorer для подобных исследований.
Например, по ссылке выше можно увидеть, что gcc/clang делают встроенный код, идентичный "ручному" сравнению для подходов через variadic non-type temlate args и через initializer_list.
При этом, для подхода через массив значений (он может быть удобен для случаев, когда нужно дать имя списку значений для переиспользования, и когда не хочется городить именнованные компайл-тайм списки) gcc даёт такой же код, а clang — таки делает массив.
А мсвц делает код "идентичный натуральному" только при подходе через variadic non-type temlate args, а для initializer_list и массива вставляет поиск (`call __std_find_trivial_4`)

A>Есть стандартная реализация?

Я такой не знаю. Для массива и initializer_list (см. примеры по ссылке выше) можно использовасть std::ranges::contains, но код получается хуже, чем через std::find. Впрочем, для msvc он один фиг неоптимальный.
Re[6]: Как записать такое в современном C++?
От: serg_joker Украина  
Дата: 17.06.23 10:54
Оценка: +1 :)
Здравствуйте, so5team, Вы писали:

S>Для себя сделаю вывод, что если стандарт не вызубрен наизусть, то безопаснее все-таки написать (false || ... || (set == val))


Ну тут же логично всё, на мой взгляд, без зазубривания.

`||...` читай как "среди перечисленного существует такое, что...". Если список "перечисленного" пуст, то и не "существует такое что".
`&&...` читай как "для каждого перечисленного верно...". Если список "перечисленного" пуст, то для всех перечисленных условие выполняется (не существует такого в перечисленном, для которого не выполняется).
Отредактировано 17.06.2023 10:57 serg_joker . Предыдущая версия .
Re[4]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 17.06.23 11:31
Оценка:
Здравствуйте, serg_joker, Вы писали:

A>>Спасибо, сразу вопросы. Лишний call выкинут все компиляторы?

_>Я бы посмотрел на те компиляторы, которые интересны. Крайне рекомендую Compiler explorer для подобных исследований.
_>Например, по ссылке выше можно увидеть, что gcc/clang делают встроенный код, идентичный "ручному" сравнению для подходов через variadic non-type temlate args и через initializer_list.
_>При этом, для подхода через массив значений (он может быть удобен для случаев, когда нужно дать имя списку значений для переиспользования, и когда не хочется городить именнованные компайл-тайм списки) gcc даёт такой же код, а clang — таки делает массив.
_>А мсвц делает код "идентичный натуральному" только при подходе через variadic non-type temlate args, а для initializer_list и массива вставляет поиск (`call __std_find_trivial_4`)

Честно сказать, я ждал ответа, что даже не сомневайся, все делают! А если надо посмотреть, всё плохо Но всё равно спасибо за ссылку!

A>>Есть стандартная реализация?

_>Я такой не знаю. Для массива и initializer_list (см. примеры по ссылке выше) можно использовасть std::ranges::contains, но код получается хуже, чем через std::find. Впрочем, для msvc он один фиг неоптимальный.

Чего только нет в std... Ничего нет!
Re[5]: Как записать такое в современном C++?
От: serg_joker Украина  
Дата: 17.06.23 11:54
Оценка: 3 (1) +1
Здравствуйте, Alekzander, Вы писали:

A>Честно сказать, я ждал ответа, что даже не сомневайся, все делают! А если надо посмотреть, всё плохо Но всё равно спасибо за ссылку!

Сомневаться и проверять вообще полезно, не только в программировании.

A>Чего только нет в std... Ничего нет!

В std Много чего нет, но много чего и есть. И список этого "чего есть" растёт, хоть и не с такой скоростью (или не с тем качеством), как этого кому-нибудь может хотеться.
А есть языки, в которых есть всё, чего хочется и в самом языке, и в стандартной библиотеке? Если да, то будет разумным переходить на них.
Если нет, то стоит принять неидеальность рабочего инструмента и развивать свои навыки его использования.
Re[5]: Как записать такое в современном C++?
От: flаt  
Дата: 18.06.23 09:33
Оценка:
Здравствуйте, vopl, Вы писали:


V>https://timsong-cpp.github.io/cppwp/temp.variadic#tab:temp.fold.empty


false для пустого || логичен, а вот true для пустого && весьма спорно, однако.
Re: Как записать такое в современном C++?
От: student__  
Дата: 19.06.23 08:55
Оценка: +3 -1
Здравствуйте, Alekzander, Вы писали:

A>С моей точки зрения это глупость. Код надо писать так, чтобы его нельзя было записать неправильно хоть в строчку, хоть столбиком, табличкой, крестиком, козликом и т.д.


Юношеский максимализм.
Re[6]: Как записать такое в современном C++?
От: Кодт Россия  
Дата: 19.06.23 09:28
Оценка: +4
Здравствуйте, flаt, Вы писали:

V>>https://timsong-cpp.github.io/cppwp/temp.variadic#tab:temp.fold.empty


F>false для пустого || логичен, а вот true для пустого && весьма спорно, однако.


Не спорно.

1) Законы Де Моргана.

OR(p1,...,pn) = not AND(not p1, ..., not pn)

2) Алгебра.

OP(p1,...,pn) = e op OP(p1,...,pn) = OP(e,p1,...,pn) = e op p1 op ... op pn

Для or нейтралью является false, для and — true.
(А также — для сложения 0, для умножения 1).
Перекуём баги на фичи!
Re: Как записать такое в современном C++?
От: BSOD  
Дата: 19.06.23 09:34
Оценка: :)
Здравствуйте, Alekzander, Вы писали:

A>Как это записать в современном C++, чтобы не было performance penalty? Без конструирования контейнера и т.п. Нормальных макросов же (как в Немерле), насколько я понимаю, не завезли?


switch(errno)
    case EAGAIN:
    case EWOULDBLOCK:
    case EINTR:
    case ENOSPC:
    case ENOBUFS:
    case ENOMEM:
    {...}
Sine vilitate, sine malitiosa mente
Re[2]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 20.06.23 06:21
Оценка:
Здравствуйте, student__, Вы писали:

A>>С моей точки зрения это глупость. Код надо писать так, чтобы его нельзя было записать неправильно хоть в строчку, хоть столбиком, табличкой, крестиком, козликом и т.д.


__>Юношеский максимализм.


Сочувствую.
Re[2]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 20.06.23 06:23
Оценка:
Здравствуйте, 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 это верный способ сделать больше ошибок, а не меньше.
Re[5]: Как записать такое в современном C++?
От: Maniacal Россия  
Дата: 20.06.23 08:05
Оценка: +1
Здравствуйте, Alekzander, Вы писали:

A>Чего только нет в std... Ничего нет!


Похоже на отсылку к Жванецкому

У нас чего только может не быть. У нас всего может не быть. У нас чего только не захочешь, того может и не быть.

— Михаил Жванецкий
Re: Как записать такое в современном C++?
От: Dair Россия  
Дата: 20.06.23 08:39
Оценка:
Здравствуйте, Alekzander, Вы писали:

A> if (!(errno == EAGAIN || EWOULDBLOCK ||

A> errno == EINTR || errno == ENOSPC ||
A> errno == ENOBUFS || errno == ENOMEM)) {

Коллеги выше красиво написали, но почему-то упустили тот факт, что EWOULDBLOCK не находится в списке значений, которые не должен принимать errno.
Re[2]: Как записать такое в современном C++?
От: Sm0ke Россия ksi
Дата: 30.06.23 19:13
Оценка: 2 (1)
Здравствуйте, Dair, Вы писали:

D>Здравствуйте, Alekzander, Вы писали:


A>> if (!(errno == EAGAIN || EWOULDBLOCK ||

A>> errno == EINTR || errno == ENOSPC ||
A>> errno == ENOBUFS || errno == ENOMEM)) {

D>Коллеги выше красиво написали, но почему-то упустили тот факт, что EWOULDBLOCK не находится в списке значений, которые не должен принимать errno.


Этот фрагмент, как упоминается в исходной статье от студии PVS, содержит ошибку.

В исправленной версии там именно сравнение с errno.
Re[2]: Как записать такое в современном C++?
От: Sm0ke Россия ksi
Дата: 30.06.23 19:18
Оценка:
Здравствуйте, vopl, Вы писали:

V>например


V>
V>#include <errno.h>

V>template <auto... set>
V>bool isin(auto val)
V>{
V>    return (false || ... || (set == val));
V>}

V>int main() 
V>{
V>    // 
V>    if(isin<EAGAIN,
V>            EWOULDBLOCK,
V>            EINTR,
V>            ENOSPC,
V>            ENOBUFS,
V>            ENOMEM>(errno))
V>    {
V>        return 0;
V>    }

V>    return 1;
V>}
V>


Вот бы уменьшить число сравнений с помощью бинарного поиска в упорядоченном ряде значений.
Взять какой-нибудь constexpr set и вызвать метод contains().

Есть например https://github.com/serge-sans-paille/frozen , но я его не пробовал.
Отредактировано 30.06.2023 19:19 Sm0ke . Предыдущая версия .
Re: Как записать такое в современном C++?
От: B0FEE664  
Дата: 03.07.23 13:30
Оценка: 12 (2) +1
Здравствуйте, Alekzander, Вы писали:

A>Как это записать в современном C++, чтобы не было performance penalty?


Для такого случая у меня есть специальный класс:
template<class T>
class The
{
    public:
        constexpr explicit The(const T& x) noexcept
          : _x(x)
        {
        }

        template <class... TArgs>
        bool one_of(TArgs&&... args) const
        {
            return ((_x == args) || ...);
        }

        template <auto... VArgs>
        bool one_of() const
        {
            return ((_x == VArgs) || ...);
        }

    public:
        const T& _x;
};


На мой взгляд такой код легче читать, чем вариант с функцией
Автор: vopl
Дата: 17.06.23
:
int main() 
{
    // 
    if ( The(errno).one_of<
                           EAGAIN,
                           EWOULDBLOCK,
                           EINTR,
                           ENOSPC,
                           ENOBUFS,
                           ENOMEM
                         >()
       )
    {
        return 0;
    }

    return 1;
}
И каждый день — без права на ошибку...
Re[2]: Как записать такое в современном C++?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 03.07.23 14:22
Оценка:
Здравствуйте, 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 выдаёт ошибку.
Можно проверять препроцессором, но это ещё больше левых слов.
А в варианте с == проблемы не возникает
The God is real, unless declared integer.
Re[3]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 03.07.23 15:46
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Вот бы уменьшить число сравнений с помощью бинарного поиска в упорядоченном ряде значений.

S>Взять какой-нибудь constexpr set и вызвать метод contains().
C++ и так это способен оптимизировать до битовых полей, что бы сравнивать группами.
Re: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.07.24 20:16
Оценка: -1
Здравствуйте, Alekzander, Вы писали:

A>Код надо писать так, чтобы его нельзя было записать неправильно


Такой подход неизбежно вырождается в известное "создайте систему, которой сможет пользоваться дурак, и только дурак захочет ею пользоваться". Невозможно (и не нужно) загодя предусмотреть все мыслимые косяки, которые могут возникнуть в применении какого-либо инструмента. А вот возможность доработки инструмента предусматривать как раз полезно — ту же болгарку можно использовать и с защитным кожухом, и без него, или, наоборот, зажать в какой-нибудь привод и накрыть всю эту конструкцию общим кожухом.

В данном случае самое правильное — с одной стороны, иметь в языке средства для удобной и эффективной записи подобных конструкций, а с другой — иметь в его компиляторе предупреждения на любые подозрительные конструкции.
Re[2]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 27.07.24 20:27
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

A>>Код надо писать так, чтобы его нельзя было записать неправильно


ЕМ>Такой подход неизбежно вырождается в известное "создайте систему, которой сможет пользоваться дурак, и только дурак захочет ею пользоваться". Невозможно (и не нужно) загодя предусмотреть все мыслимые косяки, которые могут возникнуть в применении какого-либо инструмента. А вот возможность доработки инструмента предусматривать как раз полезно — ту же болгарку можно использовать и с защитным кожухом, и без него, или, наоборот, зажать в какой-нибудь привод и накрыть всю эту конструкцию общим кожухом.


ЕМ>В данном случае самое правильное — с одной стороны, иметь в языке средства для удобной и эффективной записи подобных конструкций, а с другой — иметь в его компиляторе предупреждения на любые подозрительные конструкции.


Я понял. На самом деле я дурак, и мне это не надо. Классика русскоязычных форумов по программированию ))
Re[3]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.07.24 21:49
Оценка: :))
Здравствуйте, Alekzander, Вы писали:

A>На самом деле я дурак, и мне это не надо. Классика русскоязычных форумов по программированию ))


Возможно, Вам "это" и надо, но, получив частное решение, Вы скоро столкнетесь с похожим случаем, "вот точно таким же, только другим", и для него снова потребуется частное решение. На какой итерации уже надоест, и захочется более общего?
Re[4]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 29.07.24 10:15
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Возможно, Вам "это" и надо, но, получив частное решение, Вы скоро столкнетесь с похожим случаем, "вот точно таким же, только другим", и для него снова потребуется частное решение. На какой итерации уже надоест, и захочется более общего?


В чём "частность" такого решения
Автор: B0FEE664
Дата: 03.07.23
?
И каждый день — без права на ошибку...
Re[4]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 29.07.24 12:33
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

A>>На самом деле я дурак, и мне это не надо. Классика русскоязычных форумов по программированию ))


ЕМ>Возможно, Вам "это" и надо, но, получив частное решение, Вы скоро столкнетесь с похожим случаем, "вот точно таким же, только другим", и для него снова потребуется частное решение. На какой итерации уже надоест, и захочется более общего?


Я внезапно понял, что ты просто не умеешь программировать. Зато умничаешь как Александреску.

Любой, буквально любой джун, если он хотя бы год попрограммировал, увидит тут следующее. У нас есть одна и та же операция над каждым элементом множества. Но мы записываем её не один раз, а копируем код операции для каждого элемента. Это называется "нарушение DRY" в виде копипасты. К чему приводит копипаста очень хорошо видно из статьи, на которую я сослался. Но есть и другие соображения. Например, если у нас сравнение нетривиальное, и цена его после рефакторинга выросла, в is_in_set простое развёртывание можно заменить кешированием профиля. Но это обсуждение, боюсь, требует другого уровня квалификации. Такого, когда человек сам способен разглядеть копипасту, а не ждёт, когда его натычут в неё носом.
Re[5]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 29.07.24 13:25
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>В чём "частность" такого решения
Автор: B0FEE664
Дата: 03.07.23
?


В том, что устраняется лишь один вид возможной опечатки, и реализуется так же лишь один вид сравнений (простая однородная последовательность). Во менее однородных сравнениях (например, где нужно дополнительно проверить другие условия) все останется по-прежнему.

Это не говоря уже о том, что сделать опечатку в вызове такого хитровыгнутого шаблона тоже достаточно легко, и по кучке сообщений об ошибках, которую компилятор насыпет в ответ, тоже далеко не сразу поймешь, что именно не так.
Re[6]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 29.07.24 18:30
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>В чём "частность" такого решения
Автор: B0FEE664
Дата: 03.07.23
?

ЕМ>В том, что устраняется лишь один вид возможной опечатки, и реализуется так же лишь один вид сравнений (простая однородная последовательность). Во менее однородных сравнениях (например, где нужно дополнительно проверить другие условия) все останется по-прежнему.

Видите ли, это у автора темы был вопрос про опечатку, я же писал этот класс из совсем других соображений, главным образом для компактности и выразительности кода. Обычным использованием этого класса у меня является такой код:
assert((The(fileType).one_of<0, 1, 2, 3, 4>()));
Мне легче его читать, чем пять сравнений соединённых через ||

Реже встречаются строчки вида
    if ( The(m_phase).one_of<EPhase::eIdle, EPhase::ePauseBetweenSeries>() )


Редко с дополнительным условием:
    return ! (std::isalnum(ch) || The(ch).one_of<'.', '-'>());


Для меня этот код не частный, а наоборот, слишком общий и некоторые его возможности вообще не используются. Например, вот такое его применение я не нахожу оправданным
    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;


Поэтому иногда, чтобы избежать общности, я заменяю его более частными случаями прописывая метод класса вида:
template<Mode... modeArgs>
inline bool MyClass::IsMode() const noexcept
{
    return ((m_mode == modeArgs) || ...);
}

  if ( IsMode<Mode::A>() )
    ...
  if ( IsMode<Mode::A, Mode::B>() )
    ...


Короче, я хочу сказать, что нахожу такой код чересчур общим, а никак не частным.


ЕМ>Это не говоря уже о том, что сделать опечатку в вызове такого хитровыгнутого шаблона тоже достаточно легко, и по кучке сообщений об ошибках, которую компилятор насыпет в ответ, тоже далеко не сразу поймешь, что именно не так.

Лучше получить ошибку компиляции, чем ошибку выполнения.
И каждый день — без права на ошибку...
Re[4]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 29.07.24 18:37
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>C++ и так это способен оптимизировать до битовых полей, что бы сравнивать группами.


Именно C++? Или таки один-два конкретных компилятора?
Re[7]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 29.07.24 18:59
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>assert((The(fileType).one_of<0, 1, 2, 3, 4>()));

BFE>Мне легче его читать, чем пять сравнений соединённых через ||

И Вас не смущает, что из такой формулировки, как правило, не следует, какой смысл имеет данное подмножество и, по-хорошему, нужен дополнительный комментарий?

BFE>нахожу такой код чересчур общим, а никак не частным.


Я тоже очень люблю всяческие избыточные проверки и assert'ы, но не припомню, чтоб мне приходилось в товарных количествах проверять переменные на равенство не связанным между собой значениям. Разовые проверки обычно укладываются максимум в два-три варианта или проверку вхождения в диапазон. Если же набор вариантов более сложный, как в примере у ТС, и встречается более одного раза, то гораздо правильнее сделать осмысленный предикат "значение удовлетворяет условию".

А если делать средства, как у Вас, просто для компактной и "более надежной" записи систематических выражений, можно докатиться и до замены "a + b + c" на "sum (a, b, c)".

В принципе, я вовсе не против подобных средств группировки. Но засада-то в том, что это сильно избыточно (на каждый вызов создается отдельный класс, который нигде больше не нужен), и весьма уродливо (реализация пакетов параметров шаблона, как очередное сугубо частное решение, худо-бедно годится для подобных последовательностей, а последовательности более другого вида не поймет, и их по-прежнему придется выписывать руками).
Re: Как записать такое в современном C++?
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.07.24 00:01
Оценка: 15 (1)
Здравствуйте, 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, а на некоторых — нет. Поэтому с ним приходится что-то некрасивое делать.
Re[2]: Как записать такое в современном C++?
От: reversecode google
Дата: 30.07.24 00:04
Оценка: 15 (1) -2
в православном C++
там есть std::error_code который уже имеет правильное для этого имя-значение
которое правильно задефайнено для разных ОС
Re[3]: Как записать такое в современном C++?
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.07.24 00:06
Оценка: +1
Здравствуйте, Alekzander, Вы писали:

A>Предикат через switch-case... такое. Тем более, в данном случае по невнимательности ошибок ещё больше настряпаешь, чем с простым if'ом.


Предикат черес switch-case хорош тем, что в него очень легко и естественно добавлять упущенные значения.
Re[3]: Как записать такое в современном C++?
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.07.24 00:08
Оценка:
Здравствуйте, netch80, Вы писали:

N>Тут есть одна проблемка: на большинстве систем коды EAGAIN и EWOULDBLOCK сейчас совпадают (такое вот легаси), но есть специфические, где они различны. При совпадении switch выдаёт ошибку.


А на BSD совпадает?
Re[5]: Как записать такое в современном C++?
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.07.24 00:11
Оценка:
Здравствуйте, Alekzander, Вы писали:

A>Я внезапно понял, что ты просто не умеешь программировать. Зато умничаешь как Александреску.


Женя Музыченко — очень квалифицированный программист с многолетним стажем. JFYI.
Re[8]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 30.07.24 08:00
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>assert((The(fileType).one_of<0, 1, 2, 3, 4>()));

BFE>>Мне легче его читать, чем пять сравнений соединённых через ||

ЕМ>И Вас не смущает, что из такой формулировки, как правило, не следует, какой смысл имеет данное подмножество и, по-хорошему, нужен дополнительный комментарий?


Евгений, а вас не смущает, что из такой формулировки:
switch(fileType) {
  case 0: ... break;
  case 1: ... break;
  case 2: ... break;
  case 3: ... break;
  case 4: ... break;
}

не следует какой смысл имеет данное подмножество?

ЕМ>В принципе, я вовсе не против подобных средств группировки. Но засада-то в том, что это сильно избыточно (на каждый вызов создается отдельный класс, который нигде больше не нужен)


И? Ну класс и класс. Он что, у вас есть простит? Каждый день, по три раза?

ЕМ>и весьма уродливо (реализация пакетов параметров шаблона, как очередное сугубо частное решение, худо-бедно годится для подобных последовательностей, а последовательности более другого вида не поймет, и их по-прежнему придется выписывать руками).


Еще и кофе варить не умеет и ренту не приносит.
Re[8]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 30.07.24 09:55
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>assert((The(fileType).one_of<0, 1, 2, 3, 4>()));

BFE>>Мне легче его читать, чем пять сравнений соединённых через ||

ЕМ>И Вас не смущает, что из такой формулировки, как правило, не следует, какой смысл имеет данное подмножество и, по-хорошему, нужен дополнительный комментарий?


Смущает, но что делать, если концепцию перечисления (enum) понимает примерно только каждый десятый программист, а остальные думают, что это просто набор констант? Когда коллеги закладываются на значения констант перечисления появляются вот такие дурные проверки. Комментарии тут бесполезны.
И каждый день — без права на ошибку...
Re[9]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 30.07.24 10:08
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Смущает, но что делать, если концепцию перечисления (enum) понимает примерно только каждый десятый программист, а остальные думают, что это просто набор констант?

Понимание концепция enum, дао набора констант... Постижение истины. Вот на кой подобное в инженерном деле?

BFE>Когда коллеги закладываются на значения констант перечисления появляются вот такие дурные проверки. Комментарии тут бесполезны.

Я вообще фигею — вынесли проверку в отдельную функцию с вменяемым названием и пользуйте. Не нравятся функции вынесите в макрос
Re[9]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 10:24
Оценка:
Здравствуйте, so5team, Вы писали:

S>Евгений, а вас не смущает, что из такой формулировки:

S>
S>switch(fileType) {
S>  case 0: ... break;
S>}
S>

S>не следует какой смысл имеет данное подмножество?

Смущает, конечно. Поэтому я такие конструкции, если они повторяются, стараюсь выносить в отдельные единицы.

ЕМ>>на каждый вызов создается отдельный класс, который нигде больше не нужен


S>И? Ну класс и класс. Он что, у вас есть простит? Каждый день, по три раза?


Меня раздражает то, что это все костыли, причем придуманные не студентами-недоучками, а вполне себе профессионалами, вдобавок затащенные в стандарт языка, и повсеместно рекомендуемые. А если я напишу какой-нибудь макрос с переходами в спагетти-стайл, и эти люди увидят его у меня, они станут утверждать, будто мой код "грязный", а их код, построенный, по сути, на тех же принципах — "чистый".

S>Еще и кофе варить не умеет и ренту не приносит.


А и не надо. Не о том спич.
Re[9]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 10:29
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Смущает, но что делать, если концепцию перечисления (enum) понимает примерно только каждый десятый программист, а остальные думают, что это просто набор констант?


Дык, а чему удивляться, когда именно эту концепцию упорно тащили, как минимум, до появления enum class? По-хорошему, scoped enum нужно было иметь уже в первых версиях языка. Тем более, что на нём можно было бы почти даром сделать реализацию небольших множеств, по аналогии с паскалевским set.
Re[10]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 10:36
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>вынесли проверку в отдельную функцию с вменяемым названием и пользуйте.


К функциональной реализации тут уже были претензии по поводу "лишнего call", как будто такие проверки делаются минимум десятки-сотни тысяч раз в секунду.

_>Не нравятся функции вынесите в макрос :m


Так макрос ведь тоже по-человечески не сделаешь, макропроцессор лишь чуть менее убог, чем механизм шаблонов. Если б можно было, что в макросе, что в шаблоне, псевдоциклом перебрать параметры, формируя по ходу цикла итоговую конструкцию, во многих уродливых решениях попросту не было бы нужды.
Re[10]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 30.07.24 10:49
Оценка:
Здравствуйте, kov_serg, Вы писали:

BFE>>Смущает, но что делать, если концепцию перечисления (enum) понимает примерно только каждый десятый программист, а остальные думают, что это просто набор констант?

_>Понимание концепция enum, дао набора констант... Постижение истины. Вот на кой подобное в инженерном деле?
Затем, что абстрактный код легче править и развивать. В отношении enum это означает, что добавление или удаление элемента в начало или середину перечисления не ломает уже существующий код. Если же считать, что enum — это просто набор констант, то добавление нового элемента в середину перечисления приводит к просмотру и правке всего кода, который прямо или косвенно связан с константами. Часто, если используется тупая сереализация, это вообще невозможно или ломает совместимость с предыдущими версиями.

BFE>>Когда коллеги закладываются на значения констант перечисления появляются вот такие дурные проверки. Комментарии тут бесполезны.

_>Я вообще фигею — вынесли проверку в отдельную функцию с вменяемым названием и пользуйте.
Ага. Точно: код из assert вынести в отдельную функцию! Это же гениально и главное просто: добавить отдельный файл с вменяемым названием (на придумывание вменяемого названия потратить минут 20), добавить файл в проект, добавить файл в систему контроля версий, не забыть обложить функцию дефайнами, чтобы она была выкинута при компиляции в release, а потом вызывать её из assert, спрятав от читателя все константы так, чтобы при первой правке кода забыть про добавление константы и поймав assert в рантайме исправить код функции ни разу не подумав о том, что эта проверка была написана не просто так и без этой проверки код лежащий ниже assert'a незаметно работает с ошибкой...

_>Не нравятся функции вынесите в макрос

Для меня наличие макросов означает низкое качество кода.
И каждый день — без права на ошибку...
Re[10]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 30.07.24 11:07
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Смущает, конечно. Поэтому я такие конструкции, если они повторяются, стараюсь выносить в отдельные единицы.


А если не повторяются?

ЕМ>>>на каждый вызов создается отдельный класс, который нигде больше не нужен


S>>И? Ну класс и класс. Он что, у вас есть простит? Каждый день, по три раза?


ЕМ>Меня раздражает то, что это все костыли, причем придуманные не студентами-недоучками, а вполне себе профессионалами, вдобавок затащенные в стандарт языка, и повсеместно рекомендуемые.


Т.е. вы все никак не можете смириться с той вселенной, в которой живете?

Может вам тогда к специалистам обратиться, а не изливать свою боль на RSDN-е?

ЕМ>А если я напишу какой-нибудь макрос с переходами в спагетти-стайл


То у вас нужно попросить объяснить, почему спаггети-стайл, и почему в макросах. Если не сможете, то надавать по рукам.

ЕМ> а их код, построенный, по сути, на тех же принципах


На каких-таких принципах?

S>>Еще и кофе варить не умеет и ренту не приносит.


ЕМ>А и не надо. Не о том спич.


Спич как раз о том, что не нужно требовать от инструмента того, на что он не рассчитан.

Тов.B0FEE664 привел пример кода, который решает его задачи. На универсальность не претендует. На решение абсолютно всех проблем -- тоже. Так чего вы к этому коду прицепились?

Не понимаете как работает? Ну так и не пишите в таком стиле. Пишите как можете. C++ позволяет делать одно и тоже кучей разных способов.
Re[2]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 30.07.24 11:14
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Я б вот так написал:


Pzz>
Pzz>    if (errno == EAGAIN) {
Pzz>        errno = EWOULDBLOCK;
Pzz>    }
Pzz>


Это будет трудноватенько обобщить. (Может, и к лучшему).
Re[6]: Как записать такое в современном C++?
От: Alekzander Россия  
Дата: 30.07.24 11:20
Оценка: :)
Здравствуйте, Pzz, Вы писали:

A>>Я внезапно понял, что ты просто не умеешь программировать. Зато умничаешь как Александреску.


Pzz>Женя Музыченко — очень квалифицированный программист с многолетним стажем. JFYI.


Печальные же времена переживает программирование, если квалифицированный программист с многолетним стажем не способен распознать нарушение DRY.
Re[11]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 30.07.24 11:23
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Затем, что абстрактный код легче править и развивать.

Кто сказал? Абстрактный код обычно помимо того что абстрактный так еще и обобщенный. А на практике есть куча граничных случаев. В результате получаются разные крайности.
И править не сломав может может быть не просто.

BFE>В отношении enum это означает, что добавление или удаление элемента в начало или середину перечисления не ломает уже существующий код.

enum { C0,C1,C2,C3, C_MAX };

Если добавление ломает код это значит код хреновый. А вот удаление и должно ломать, если эта константа использовалась

BFE>Если же считать, что enum — это просто набор констант, то добавление нового элемента в середину перечисления приводит к просмотру и правке всего кода, который прямо или косвенно связан с константами.

Так в enum можно явно значения объявлять:
enum { B0=1,B1=2,B2=4,B3=8,B4=16,B5=32,B6=64,B7=128 };

BFE>
BFE>Часто, если используется тупая сереализация, это вообще невозможно или ломает совместимость с предыдущими версиями.
Совместимость с предыдущими версиями это не знача компилятора. Это на совести программиста. И если стоит задача сломать совместимость её обязательно сломают.

BFE>Ага. Точно: код из assert вынести в отдельную функцию! Это же гениально и главное просто: добавить отдельный файл с вменяемым названием (на придумывание вменяемого названия потратить минут 20), добавить файл в проект, добавить файл в систему контроля версий, не забыть обложить функцию дефайнами, чтобы она была выкинута при компиляции в release, а потом вызывать её из assert, спрятав от читателя все константы так, чтобы при первой правке кода забыть про добавление константы и поймав assert в рантайме исправить код функции ни разу не подумав о том, что эта проверка была написана не просто так и без этой проверки код лежащий ниже assert'a незаметно работает с ошибкой...

Точно надо вставлять проверки с портянками констант — это же гениально и главное просто, и когда надо что-то исправить достаточно поменять во всех файла где такое уже умудрились написать.
assert-ы должны проверять инварианты, которые должны быть описаны явно и в одном месте, а не раскиданы равномерно везде.

_>>Не нравятся функции вынесите в макрос

BFE>Для меня наличие макросов означает низкое качество кода.
Ха ха ха. То что макросы досих пор есть, означает что их функционал пока нечем заменить. Вы видели реальную кодовую базу без макросов?
Re[11]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 30.07.24 11:28
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, kov_serg, Вы писали:


_>>вынесли проверку в отдельную функцию с вменяемым названием и пользуйте.


ЕМ>К функциональной реализации тут уже были претензии по поводу "лишнего call", как будто такие проверки делаются минимум десятки-сотни тысяч раз в секунду.

Так на секундочку, C++ умеет инлайнить код если это целесообразно. И потом реальный вклад подобной проверки ничтожен, на фоне доступа к оперативной памяти.

ЕМ>Так макрос ведь тоже по-человечески не сделаешь

И кто в этом виноват? Я просто написал что есть такой вариант, и на практике часто встречается. А в макросе может быть уже или код проверки или вызов функции или просто пусто. В зависимости от фазы луны define-ов.
Re[3]: Как записать такое в современном C++?
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.07.24 11:39
Оценка:
Здравствуйте, Alekzander, Вы писали:

Pzz>>
Pzz>>    if (errno == EAGAIN) {
Pzz>>        errno = EWOULDBLOCK;
Pzz>>    }
Pzz>>


A>Это будет трудноватенько обобщить. (Может, и к лучшему).


А это и не нужно обобщать. Это исключение, а не правило.
Re[12]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 30.07.24 12:09
Оценка:
Здравствуйте, kov_serg, Вы писали:

BFE>>Затем, что абстрактный код легче править и развивать.

_>Кто сказал?
Очевидно, что я.

_>Абстрактный код обычно помимо того что абстрактный так еще и обобщенный. А на практике есть куча граничных случаев. В результате получаются разные крайности.

_>И править не сломав может может быть не просто.
Сложность бывает разная: одно дело — головой думать, другое — искать иголку в стоге сена. И то и то сложно, но есть нюанс.

BFE>>В отношении enum это означает, что добавление или удаление элемента в начало или середину перечисления не ломает уже существующий код.

_>
_>enum { C0,C1,C2,C3, C_MAX };
_>

_>Если добавление ломает код это значит код хреновый.
Да уже и так видно, без добавления, что, таки, да.

_>А вот удаление и должно ломать, если эта константа использовалась

очевидно.

BFE>>Если же считать, что enum — это просто набор констант, то добавление нового элемента в середину перечисления приводит к просмотру и правке всего кода, который прямо или косвенно связан с константами.

_>Так в enum можно явно значения объявлять:
_>
_>enum { B0=1,B1=2,B2=4,B3=8,B4=16,B5=32,B6=64,B7=128 };
_>

Я об этом и пишу.

BFE>>

BFE>>Часто, если используется тупая сереализация, это вообще невозможно или ломает совместимость с предыдущими версиями.
_>Совместимость с предыдущими версиями это не знача компилятора. Это на совести программиста. И если стоит задача сломать совместимость её обязательно сломают.
Я где-то в ветке писал про компилятор?

BFE>>Ага. Точно: код из assert вынести в отдельную функцию! Это же гениально и главное просто: добавить отдельный файл с вменяемым названием (на придумывание вменяемого названия потратить минут 20), добавить файл в проект, добавить файл в систему контроля версий, не забыть обложить функцию дефайнами, чтобы она была выкинута при компиляции в release, а потом вызывать её из assert, спрятав от читателя все константы так, чтобы при первой правке кода забыть про добавление константы и поймав assert в рантайме исправить код функции ни разу не подумав о том, что эта проверка была написана не просто так и без этой проверки код лежащий ниже assert'a незаметно работает с ошибкой...

_>Точно надо вставлять проверки с портянками констант — это же гениально и главное просто, и когда надо что-то исправить достаточно поменять во всех файла где такое уже умудрились написать.
Если на значение констант полагаются, то — да, везде при использовании должны быть проверки.

_>assert-ы должны проверять инварианты, которые должны быть описаны явно и в одном месте, а не раскиданы равномерно везде.

Для этого есть static_assert'ы
А обычные assert'ы я использую для проверки входных параметров функции для проверки предусловий.

_>>>Не нравятся функции вынесите в макрос

BFE>>Для меня наличие макросов означает низкое качество кода.
_>Ха ха ха. То что макросы досих пор есть, означает что их функционал пока нечем заменить.
Да, к сожалению, часто функционал нечем заменить и приходится писать руками.

_>Вы видели реальную кодовую базу без макросов?

Да.
И каждый день — без права на ошибку...
Отредактировано 30.07.2024 12:20 B0FEE664 . Предыдущая версия .
Re[10]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 30.07.24 12:15
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Дык, а чему удивляться, когда именно эту концепцию упорно тащили, как минимум, до появления enum class? По-хорошему, scoped enum нужно было иметь уже в первых версиях языка. Тем более, что на нём можно было бы почти даром сделать реализацию небольших множеств, по аналогии с паскалевским set.


Вот ввели enum class и что? 90% программистов продолжают прописывать значения элементам и кастить. Если запретить прописывать значения, то почти все из них просто перестанет пользоваться перечислением.
И каждый день — без права на ошибку...
Re[11]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 14:40
Оценка:
Здравствуйте, so5team, Вы писали:

S>А если не повторяются?


Тогда там везде разная логика обработки. И внести ошибку, забыв добавить к сравнению одно из значений, или добавив не то, что нужно, не сложнее, чем сделать опечатку.

S>вы все никак не можете смириться с той вселенной, в которой живете?


А Вы действительно воображаете, будто это "вселенная", а не просто засилье подхода "делаем потому, что можем"?

S>у вас нужно попросить объяснить, почему спаггети-стайл, и почему в макросах. Если не сможете, то надавать по рукам.


Тут весь вопрос в том, что понимается под "сможете". Ответить-то я всяко смогу, но мой ответ может не понравится вопрошающим, как верна и обратная ситуация. И тут на первое место выходит эта самая возможность надавать по рукам — в смысле, насколько это осуществимо.

ЕМ>> а их код, построенный, по сути, на тех же принципах


S>На каких-таких принципах?


На принципах применения недоделанных костылей, объединенных не столько разумной логикой, сколько принципами минимализма — "как бы сделать инструмент попроще, чтоб толпа бесплатных энтузиастов придумала для него побольше применений".

S>Спич как раз о том, что не нужно требовать от инструмента того, на что он не рассчитан.


То есть, STL, Boost, Loki применяют инструмент именно для того, на что он был рассчитан? А в последнем обсуждаемом примере одноразовый шаблонный класс создается тоже для того, на что была рассчитана идея классов?

S>Тов.B0FEE664 привел пример кода, который решает его задачи. На универсальность не претендует.


Из высказываний тов. B0FEE664 следует как раз обратное.

S>Так чего вы к этому коду прицепились?


Того, что писать такой код поощряет уродливый механизм реализации "современных" возможностей шаблонов, выдаваемый за передовой.

S>Не понимаете как работает?


Понимаю.

S>Пишите как можете. C++ позволяет делать одно и тоже кучей разных способов.


Так я всегда пишу, как могу, но C++ постоянно вынуждает делать вроде бы простые вещи непременно через задницу. То, что непосредственно в тексте программы это иногда выглядит более-менее читабельно, а задница заботливо вынесена в отдельные заголовки, ситуации не меняет.
Re[11]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 14:47
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>код из assert вынести в отдельную функцию! Это же гениально и главное просто: добавить отдельный файл с вменяемым названием (на придумывание вменяемого названия потратить минут 20), добавить файл в проект, добавить файл в систему контроля версий, не забыть обложить функцию дефайнами, чтобы она была выкинута при компиляции в release, а потом вызывать её из assert


Я последние лет двадцать делаю именно так. То есть, если инвариант в виде мало-мальски сложного выражения сугубо "местный", то выражение в assert указывается, как есть; если же инвариант встречается в двух-трех разных местах и больше, то для него определяется функция с соответствующим смысловым названием, существующая только в отладочных сборках.

BFE>Для меня наличие макросов означает низкое качество кода.


Поди, и наличие goto тоже?
Re[11]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 14:53
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Вот ввели enum class и что? 90% программистов продолжают прописывать значения элементам и кастить.


Теперь это их вина, а не создателей стандартов и компиляторов. У тех, кто имеет доступ к рычагам воздействия, есть выбор — заставлять или не обращать внимания.

BFE>Если запретить прописывать значения, то почти все из них просто перестанет пользоваться перечислением.


А зачем запрещать-то? Наоборот, от компилятора нужна возможность какой-то работы, отличной от ручной, с набором значений, заданных в пределах отдельного enum, хоть явно, хоть неявно.
Re[12]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 30.07.24 15:33
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

S>>А если не повторяются?


ЕМ>Тогда там везде разная логика обработки.


Спасибо, Кэп. Вопрос был не в этом.

ЕМ>И внести ошибку, забыв добавить к сравнению одно из значений, или добавив не то, что нужно, не сложнее, чем сделать опечатку.


Сложнее. Семантическая (да и синтаксическая) нагрузка разная.

S>>вы все никак не можете смириться с той вселенной, в которой живете?


ЕМ>А Вы действительно воображаете, будто это "вселенная", а не просто засилье подхода "делаем потому, что можем"?


Я не воображаю, я это ощущаю как данность: в этой вселенной мы имеем тот C++, который смогли сделать. Хотели разные люди разного, предлагали и рекламировали разное, даже реализовывали в компиляторах в качестве расширений разное. А в итоге имеем то, что имеем. Потому, что другого не смогли.

S>>у вас нужно попросить объяснить, почему спаггети-стайл, и почему в макросах. Если не сможете, то надавать по рукам.


ЕМ>Тут весь вопрос в том, что понимается под "сможете".


То и понимаете: сможете ли объяснить так, что другие члены команды сочтут ваши объяснения достаточными. Или же останетесь на уровне "мне так удобнее".

Хотя о чем это я? Где вы, и где команды.

ЕМ>На принципах применения недоделанных костылей, объединенных не столько разумной логикой, сколько принципами минимализма — "как бы сделать инструмент попроще, чтоб толпа бесплатных энтузиастов придумала для него побольше применений".


А если не бредить общими словами и показать пример? Ну типа вот на макросах, а вот на тех же принципах на шаблонах?

S>>Спич как раз о том, что не нужно требовать от инструмента того, на что он не рассчитан.


ЕМ>То есть, STL, Boost, Loki применяют инструмент именно для того, на что он был рассчитан?


STL, Boost и Loki -- это сами инструменты. И да, они используют базовый C++ именно для того, на что он был рассчитан.

EM>А в последнем обсуждаемом примере одноразовый шаблонный класс создается тоже для того, на что была рассчитана идея классов?


Во-первых, он не одноразовый.

Во-вторых, да, там используется идея шаблона класса.

S>>Тов.B0FEE664 привел пример кода, который решает его задачи. На универсальность не претендует.


ЕМ>Из высказываний тов. B0FEE664 следует как раз обратное.


Цитату можно?

S>>Так чего вы к этому коду прицепились?


ЕМ>Того, что писать такой код поощряет уродливый механизм реализации "современных" возможностей шаблонов, выдаваемый за передовой.


Если вы не смогли этот механизм освоить, то это не значит что:

a) он уродливый;
b) он что-то поощряет или нет;
c) он кем-то выдается за передовой.

Человек сделал так, как ему удобно. Результат понятен. Как пользоваться тоже понятно. Цель, можно сказать, достигнута.

S>>Пишите как можете. C++ позволяет делать одно и тоже кучей разных способов.


ЕМ>Так я всегда пишу, как могу, но C++ постоянно вынуждает делать вроде бы простые вещи непременно через задницу.


Без примеров нещитово.
Re[12]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 30.07.24 16:33
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>Вот ввели enum class и что? 90% программистов продолжают прописывать значения элементам и кастить.

ЕМ>Теперь это их вина, а не создателей стандартов и компиляторов. У тех, кто имеет доступ к рычагам воздействия, есть выбор — заставлять или не обращать внимания.
Но какой тогда смысл в enum class?

BFE>>Если запретить прописывать значения, то почти все из них просто перестанет пользоваться перечислением.

ЕМ>А зачем запрещать-то?
Чтобы не было соблазна их использовать.

ЕМ>Наоборот, от компилятора нужна возможность какой-то работы, отличной от ручной, с набором значений, заданных в пределах отдельного enum, хоть явно, хоть неявно.

Зачем перечислению значения?
И каждый день — без права на ошибку...
Re[13]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 16:39
Оценка:
Здравствуйте, so5team, Вы писали:

ЕМ>>И внести ошибку, забыв добавить к сравнению одно из значений, или добавив не то, что нужно, не сложнее, чем сделать опечатку.


S>Сложнее. Семантическая (да и синтаксическая) нагрузка разная.


Я уже подчеркивал, что бороться нужно в первую очередь с теми ошибками, которые наиболее вероятны (и в среднем, и в конкретных условиях), а не получили наибольшую известность по тем или иным причинам. Большинство же продолжает опасаться того, о чем больше говорят и пишут, а не того, что наиболее вероятно у них самих.

S>Ну типа вот на макросах, а вот на тех же принципах на шаблонах?


Так приводили ж примеры еще в прошлом году — и на макросах, и на шаблонах. И то, и другое применимо только к частным случаям, в разной мере уродливо, но макросы свыше давно уже объявлены безусловным злом, а шаблоны, даже самые ужасные — безусловным благом.

S>STL, Boost и Loki -- это сами инструменты. И да, они используют базовый C++ именно для того, на что он был рассчитан.


Где можно почитать о том, как в C++ рассчитывали на использование побочных эффектов шаблонов?

EM>>А в последнем обсуждаемом примере одноразовый шаблонный класс создается тоже для того, на что была рассчитана идея классов?


S>Во-первых, он не одноразовый.


В том виде, в каком его применяет автор — одноразовый.

S>Во-вторых, да, там используется идея шаблона класса.


А зачем там класс? По-хорошему, здесь нужен просто шаблон функции.

S>>>Тов.B0FEE664 привел пример кода, который решает его задачи. На универсальность не претендует.


ЕМ>>Из высказываний тов. B0FEE664 следует как раз обратное.


S>Цитату можно?


"Для меня этот код не частный, а наоборот, слишком общий...
Автор: B0FEE664
Дата: 29.07.24
"

S>Если вы не смогли этот механизм освоить


Я не хочу его осваивать, он вызывает у меня отвращение, поскольку являет собой нагромождение костылей, крайне слабо связанных между собой, с предельно ужатым, и оттого многократно перегруженным синтаксисом. Это не тот случай, когда краткость — сестра таланта.

ЕМ>>C++ постоянно вынуждает делать вроде бы простые вещи непременно через задницу.


S>Без примеров нещитово.


Тут уже кучу примеров навалили, Вам мало? Поскольку Вам они кажутся вполне себе логичными и даже изящными, какой смысл приводить дополнительные?

Знаете, это весьма похоже на "развитие современного искусства". Еще не так давно оставались хоть какие-то опорные точки, позволяющие отличить шедевр от говнища, но теперь все определяется исключительно мнением активного большинства. Даже если численно оно будет абсолютным меньшинством, именно его активность, громкость и упорство позволят признать "шедевром" все, что угодно.
Re[13]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.07.24 17:01
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>какой тогда смысл в enum class?


Прежде всего — в инкапсуляции идентификаторов в enum. А уж как там заданы значения — уже второй вопрос.

Кстати, отсутствие автоматического определения количества элементов, наименьшего и наибольшего значения в enum — тоже очевидный недостаток.

BFE>Зачем перечислению значения?


Для случаев, когда какие-то значения имеют особый смысл. Простейший случай — выделить группы (варианты успешного завершения, предупреждения, ошибки и т.п.). Да, придется закладываться на предельный размер диапазона, но иначе-то еще менее удобно.

Еще в ряде случаев полезно иметь значения, имеющие в младших разрядах уникальные последовательные номера, но с возможным добавлением дополнительных старших разрядов в качестве признаков. Через enum такого не сделать — приходится выписывать пачки обычных определений, вручную следя за монотонным возрастанием номеров. Ежли кто добавит новый элемент в середину, не перенумеровав остальные — будет грустно.
Re[14]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 30.07.24 17:21
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, B0FEE664, Вы писали:


BFE>>какой тогда смысл в enum class?

ЕМ>Прежде всего — в инкапсуляции идентификаторов в enum. А уж как там заданы значения — уже второй вопрос.
ЕМ>Кстати, отсутствие автоматического определения количества элементов, наименьшего и наибольшего значения в enum — тоже очевидный недостаток.

Так и раньше ни кто не запрещал идентификаторы запихивать внутрь класса вместе с доп. информацией
struct E {
  enum { C0,C1,C2, C_MAX };
  static const char* to_chars(int id) {
    switch(id) {
    case C0: return "C0";
    case C1: return "C1";
    case C2: return "C2";
    }
    return 0;
  }
  static int count() { return C_MAX; }
  static int min() { return C0; }
  static int max() { return C_MAX-1; }
};

struct B {
  enum { B1=1,B2=2,B3=4, B_MASK=B1|B2|B3 };
  static void fmt(int v, void (*write)(void* ctx,const char* text), void *ctx) {
    if (v&B1) write(ctx,"B1");
    if (v&B2) write(ctx,"B2");
    if (v&B3) write(ctx,"B3");
  }
  static int mask() { return B_MASK; };
};



ЕМ>Еще в ряде случаев полезно иметь значения, имеющие в младших разрядах уникальные последовательные номера, но с возможным добавлением дополнительных старших разрядов в качестве признаков. Через enum такого не сделать — приходится выписывать пачки обычных определений, вручную следя за монотонным возрастанием номеров. Ежли кто добавит новый элемент в середину, не перенумеровав остальные — будет грустно.
Re[14]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 30.07.24 18:49
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>И внести ошибку, забыв добавить к сравнению одно из значений, или добавив не то, что нужно, не сложнее, чем сделать опечатку.


S>>Сложнее. Семантическая (да и синтаксическая) нагрузка разная.


ЕМ>Я уже подчеркивал, что бороться нужно в первую очередь с теми ошибками, которые наиболее вероятны (и в среднем, и в конкретных условиях)


Да что вы говорите?!

А тяжесть последствий от ошибки оценивать вообще не нужно?

А стоимость устранения? Например, есть класс ошибок X, стоимость устранения этого класса 1 человеко-час на весь проект. Т.е. даже не копейки, а доли копейки. Полагаю, класс ошибок X нужно вообще не рассматривать, т.к. их вероятность ниже какого-то порога.

Отлично, Евгений, отлично. Жгите. Вы можете, я верю.

ЕМ>Большинство же продолжает опасаться того, о чем больше говорят и пишут, а не того, что наиболее вероятно у них самих.


Какое большинство, актитесь. Вы в одиночку пишете код в который считанные единицы заглядывают. Откуда у вас знания о "большинстве"? Из обсуждений на RSDN-е?

S>>Ну типа вот на макросах, а вот на тех же принципах на шаблонах?


ЕМ>Так приводили ж примеры еще в прошлом году — и на макросах, и на шаблонах. И то, и другое применимо только к частным случаям, в разной мере уродливо, но макросы свыше давно уже объявлены безусловным злом, а шаблоны, даже самые ужасные — безусловным благом.


Т.е. конкретных примеров не будет? Мне нужно перелопачивать все это обсуждение и выискивать примеры самостоятельно?

S>>STL, Boost и Loki -- это сами инструменты. И да, они используют базовый C++ именно для того, на что он был рассчитан.


ЕМ>Где можно почитать о том, как в C++ рассчитывали на использование побочных эффектов шаблонов?


Позволю себе ответить вопросом на вопрос: а что, STL, Boost и Loki базируются на побочных эффектах шаблонов? С примерами, пожалуйста. Это во-первых.

Во-вторых, ну вот есть некие побочные эффекты. Вполне себе легальный способ. Другого нет. Может быть когда-нибудь в будущем что-то будет, может быть нет. Зачем отказываться и не использовать то что есть здесь и сейчас, если это решает задачу наилучшим образом?

У меня есть всего две причины, которые для меня самого временами актуальны:

1) сложность кода превышает способности даже самых опытных и продвинутых членов команды;
2) сложность кода ведет к крахам компиляторов с internal compiler error.

Если эти причины не актуальны, то зачем игнорировать?

Всяко лучше, чем базироваться на нестандартных расширениях от Microsoft.

EM>>>А в последнем обсуждаемом примере одноразовый шаблонный класс создается тоже для того, на что была рассчитана идея классов?


S>>Во-первых, он не одноразовый.


ЕМ>В том виде, в каком его применяет автор — одноразовый.


У вас явно свои представления об "одноразовости". Сам автор привел несколько примеров использования. Если это для вас "одноразовый", то на RSDN-е вам не помогут.

S>>Во-вторых, да, там используется идея шаблона класса.


ЕМ>А зачем там класс? По-хорошему, здесь нужен просто шаблон функции.


Мы не видим всех сценариев использования.

S>>>>Тов.B0FEE664 привел пример кода, который решает его задачи. На универсальность не претендует.


ЕМ>>>Из высказываний тов. B0FEE664 следует как раз обратное.


S>>Цитату можно?


ЕМ>"Для меня этот код не частный, а наоборот, слишком общий...
Автор: B0FEE664
Дата: 29.07.24
"


Ну так перечитайте. Автор пишет, что класс общий, потому, что допускает даже такие сравнения, на которые сам автор не рассчитывал. Универсальности нет, т.к. все сценарии использования в рамках одной и той же задачи -- проверить, входит ли текущее значение в список предопределенных значений.

S>>Если вы не смогли этот механизм освоить


ЕМ>Я не хочу его осваивать, он вызывает у меня отвращение


Да фиолетово что вы хотите. Есть инструмент со своими качествами и возможностями. Этот инструмент можно освоить и применять там, где это выгодно. Либо можно ныть на RSDN-е о том, как вокруг все коряво и убого.

ЕМ>>>C++ постоянно вынуждает делать вроде бы простые вещи непременно через задницу.


S>>Без примеров нещитово.


ЕМ>Тут уже кучу примеров навалили, Вам мало? Поскольку Вам они кажутся вполне себе логичными и даже изящными, какой смысл приводить дополнительные?


Можете привести пример и сказать "вот здесь плохо потому-то и потому-то?"
Если можете, тогда будет что обсуждать.

Но вряд ли сможете. Ведь это же в предмете придется разбираться, а не уходить в аналогии про современное искусство.

ЕМ>Знаете, это весьма похоже на "развитие современного искусства".


Завязывали бы вы с бла-бла-бла.
Re[14]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 31.07.24 14:27
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>какой тогда смысл в enum class?

ЕМ>Прежде всего — в инкапсуляции идентификаторов в enum. А уж как там заданы значения — уже второй вопрос.
namespace Machine
{
enum Mode
{
  eAuto, eManual
};
}
typedef Machine::Mode MachineMode;



ЕМ>Кстати, отсутствие автоматического определения количества элементов, наименьшего и наибольшего значения в enum — тоже очевидный недостаток.

Вроде бы (ещё не дочитал), это собираются, наконец, сделать.

BFE>>Зачем перечислению значения?

ЕМ>Для случаев, когда какие-то значения имеют особый смысл. Простейший случай — выделить группы (варианты успешного завершения, предупреждения, ошибки и т.п.). Да, придется закладываться на предельный размер диапазона, но иначе-то еще менее удобно.
Что ещё за "предельный размер диапазона"?

ЕМ>Еще в ряде случаев полезно иметь значения, имеющие в младших разрядах уникальные последовательные номера, но с возможным добавлением дополнительных старших разрядов в качестве признаков. Через enum такого не сделать — приходится выписывать пачки обычных определений, вручную следя за монотонным возрастанием номеров. Ежли кто добавит новый элемент в середину, не перенумеровав остальные — будет грустно.


Пожалуйста, ответьте не на вопрос: какое отношение это имеет к перечислению?
И каждый день — без права на ошибку...
Re[15]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 31.07.24 14:46
Оценка:
Здравствуйте, B0FEE664, Вы писали:

ЕМ>>Прежде всего — в инкапсуляции идентификаторов в enum.

BFE>
BFE>namespace Machine
BFE>enum Mode
BFE>


Ну коряво же. Давайте еще вспомним, что в раннем C имена полей структур тоже были глобальными, и починили это чуть ли не после появления C++. Может, Страуструпу не нужно было ограничивать область видимости членов класса, нехай бы все были в глобальной?

BFE>Что ещё за "предельный размер диапазона"?


Когда в enum определяется несколько групп констант, в каждой группе значения идут подряд, но между группами делаются промежутки на случай добавления новых значений.

BFE>какое отношение это имеет к перечислению?


Прямое. Перечисление лежит в основе создания констант, а явное задание значений позволяет в отдельных случаях отступать от этого порядка. Если [почти] все константы имеют явно заданные значения, то и не стоит их определять в enum.

Если бы enum изначально был похож на паскалевский перечислимый тип, давал возможность узнавать количество элементов и создавать множества, я бы не стал требовать от него явного задания значений. Но раз уж его реализовали в виде примитивного средства с дополнительными возможностями, то грех не пользоваться.
Re[16]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 31.07.24 15:01
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>Прежде всего — в инкапсуляции идентификаторов в enum.

BFE>>
BFE>>namespace Machine
BFE>>enum Mode
BFE>>


ЕМ>Ну коряво же.

Почему?

ЕМ>Давайте еще вспомним, что в раннем C имена полей структур тоже были глобальными, и починили это чуть ли не после появления C++. Может, Страуструпу не нужно было ограничивать область видимости членов класса, нехай бы все были в глобальной?

Давайте. Что делать, если в двух библиотеках совпадают названия (но не элементы) у enum class?

BFE>>какое отношение это имеет к перечислению?

ЕМ>Прямое. Перечисление лежит в основе создания констант, а явное задание значений позволяет в отдельных случаях отступать от этого порядка. Если [почти] все константы имеют явно заданные значения, то и не стоит их определять в enum.
Вообще-то в основе создания констант лежит #define

ЕМ>Если бы enum изначально был похож на паскалевский перечислимый тип, давал возможность узнавать количество элементов и создавать множества, я бы не стал требовать от него явного задания значений.

Судя по предыдущему абзацу и улыбке — стали бы.

ЕМ> Но раз уж его реализовали в виде примитивного средства с дополнительными возможностями, то грех не пользоваться.

То есть использовать перечисления для констант — это не грех (несмотря на то, что ключевое слово const существует довольно давно), а использовать SFINAE — грех? Не находите, что это не последовательная позиция?
И каждый день — без права на ошибку...
Re[17]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 31.07.24 15:18
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>То есть использовать перечисления для констант — это не грех (несмотря на то, что ключевое слово const существует довольно давно), а использовать SFINAE — грех? Не находите, что это не последовательная позиция?

Ваш const не совсем const, поэтому и используют другие способы
int fn(int i) {
  const int c1=1;
  enum { c2=2 };
  switch(i) {
  //case c1: return c1; // error: label does not reduce to an integer constant
  case c2: return c2;
  }
  return 0;
}
Re[17]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 31.07.24 16:08
Оценка:
Здравствуйте, B0FEE664, Вы писали:

ЕМ>>Ну коряво же.

BFE>Почему?

Потому, что enum создает группу идентификаторов, объединенных общей идеей, а следовательно — областью видимости, пусть и неявной. Никто ж не создает {One, Two, Three}. Почему в обсуждаемом примере все идентификаторы начинаются на E?

BFE>Что делать, если в двух библиотеках совпадают названия (но не элементы) у enum class?


То же самое, как если совпадают имена классов, глобальных переменных, функций. Почему enum должен выбиваться из этой схемы?

BFE>Вообще-то в основе создания констант лежит #define


Вообще-то это не "константы", а "макроопределения". Это делалось (и посейчас нередко делается) исключительно от нехватки языковых средств.

BFE>То есть использовать перечисления для констант — это не грех (несмотря на то, что ключевое слово const существует довольно давно)


Когда const только появилось, большинство компиляторов понимало его исключительно в смысле "неизменяемый объект", и даже при максимальной оптимизации не всегда заменяло на литеральную константу. Заменять всегда стали лишь где-то в середине 90-х, если не ошибаюсь.

BFE>использовать SFINAE — грех?


Конечно, использовать SFINAE для выбора наиболее общего варианта шаблона — не грех, оно для этого и создавалось. И даже использовать SFINAE в сочетании с трюками, возможность которых изначально не закладывалась в механизм — тоже не грех, если это делается или для демонстрации забавного эффекта, или сугубо для себя, или сугубо временно, в ожидании реализации соответствующих функций в компиляторах. А вот делать такие трюки нормой жизни, да еще рекомендовать их для изучения, освоения и повсеместного применения — не простой грех, а очень тяжкий. Подход называется "ну ведь получилось же, и работает, какие претензии?".
Re[18]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 31.07.24 16:11
Оценка:
Здравствуйте, kov_serg, Вы писали:

BFE>>То есть использовать перечисления для констант — это не грех (несмотря на то, что ключевое слово const существует довольно давно), а использовать SFINAE — грех? Не находите, что это не последовательная позиция?

_>Ваш const не совсем const, поэтому и используют другие способы
А он не мой, это не я стандарт писал.
И каждый день — без права на ошибку...
Re[18]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 31.07.24 16:21
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>Ну коряво же.

BFE>>Почему?
ЕМ>Потому, что enum создает группу идентификаторов, объединенных общей идеей, а следовательно — областью видимости, пусть и неявной. Никто ж не создает {One, Two, Three}.
И для этого они лежат в namespace.

ЕМ> Почему в обсуждаемом примере все идентификаторы начинаются на E?

Предполагаю, что это сокращение от слова ERROR.

BFE>>Что делать, если в двух библиотеках совпадают названия (но не элементы) у enum class?

ЕМ>То же самое, как если совпадают имена классов, глобальных переменных, функций. Почему enum должен выбиваться из этой схемы?
Вот если их поместить в отдельный namespace, то они и не будут выбиваться из схемы (раз уж изначально это не было сделано)

BFE>>Вообще-то в основе создания констант лежит #define

ЕМ>Вообще-то это не "константы", а "макроопределения". Это делалось (и посейчас нередко делается) исключительно от нехватки языковых средств.
Согласен. Но это никак не отменяет, что использовать #define для констант уместнее, чем enum.

BFE>>использовать SFINAE — грех?

ЕМ>Конечно, использовать SFINAE для выбора наиболее общего варианта шаблона — не грех, оно для этого и создавалось. И даже использовать SFINAE в сочетании с трюками, возможность которых изначально не закладывалась в механизм — тоже не грех, если это делается или для демонстрации забавного эффекта, или сугубо для себя, или сугубо временно, в ожидании реализации соответствующих функций в компиляторах. А вот делать такие трюки нормой жизни, да еще рекомендовать их для изучения, освоения и повсеместного применения — не простой грех, а очень тяжкий. Подход называется "ну ведь получилось же, и работает, какие претензии?".

Вот сейчас уже есть нормальные константы. Вы перестали использовать перечисления для того, чтобы задавать константы?
И каждый день — без права на ошибку...
Re[19]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 31.07.24 16:35
Оценка:
Здравствуйте, B0FEE664, Вы писали:

ЕМ>>Потому, что enum создает группу идентификаторов, объединенных общей идеей


BFE>И для этого они лежат в namespace.


Тогда почему там не лежат члены классов?

ЕМ>>Почему в обсуждаемом примере все идентификаторы начинаются на E?


BFE>Предполагаю, что это сокращение от слова ERROR.


А зачем нужен такой префикс? Уникальность обеспечивается и без него.

BFE>Вот если их поместить в отдельный namespace, то они и не будут выбиваться из схемы (раз уж изначально это не было сделано)


Изначально, напомню, инкапсуляции не было и для полей структур. Ее тоже зря сделали?

BFE>использовать #define для констант уместнее, чем enum.


Я бы сказал, что уместнее использовать [static] const, а при невозможности — #define. Но это для разрозненных констант, от которых не ожидается какой-либо регулярности. Если же константы хотя бы в основе завязаны на перечисление, то вполне логично определять их в перечислении.

BFE>Вот сейчас уже есть нормальные константы. Вы перестали использовать перечисления для того, чтобы задавать константы?


Я ж говорил, что для "просто констант" давно использую [static] const. Но, если мне нужны константы, например, для кодов ошибок, разделенных на группы (по степени серьезности, или по ситуациям, по связанным объектам и т.п.), то альтернативой enum будет только ручной инкремент. Вы предлагаете использовать его?

Если Вы до сих пор пытаетесь провести аналогию с шаблонными трюками, то это ж несопоставимые вещи. enum — простая конструкция, ее смысл очевиден любому, кто хоть более-менее знаком с языком. Она не порождает отдаленных последствий. Ну и то, что в языке таки появился enum class, уже говорит о том, что проблему осознали, и приняли меры для ее устранения. А что появилось в языке для замены шаблонной магии? Даже if constexpr можно применить только к исполняемому коду. Это не метапрограммирование, а издевательство.
Re[18]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 31.07.24 16:56
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Ваш const не совсем const, поэтому и используют другие способы

_>
_>int fn(int i) {
_>  const int c1=1;
_>  enum { c2=2 };
_>  switch(i) {
_>  //case c1: return c1; // error: label does not reduce to an integer constant
_>  case c2: return c2;
_>  }
_>  return 0;
_>}
_>


А речь точно идет о C++?

https://wandbox.org/permlink/RWneePV7jikXCdQy
Re[19]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 31.07.24 17:15
Оценка: +1
Здравствуйте, so5team, Вы писали:

S>Здравствуйте, kov_serg, Вы писали:


_>>Ваш const не совсем const, поэтому и используют другие способы

_>>
_>>int fn(int i) {
_>>  const int c1=1;
_>>  enum { c2=2 };
_>>  switch(i) {
_>>  //case c1: return c1; // error: label does not reduce to an integer constant
_>>  case c2: return c2;
_>>  }
_>>  return 0;
_>>}
_>>


S>А речь точно идет о C++?


S>https://wandbox.org/permlink/RWneePV7jikXCdQy


А теперь представьте что константы надо объявить в header-е
extern const int a;
const int b=1;

const int a=0;

И всё приплыли. А enum-ы можно объявлять где попало и они constexpr. Правда использование таких констант в шаблонах немного не удобно. Но это проблемы шаблонов, а не констант.
Re[20]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 31.07.24 18:37
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>Потому, что enum создает группу идентификаторов, объединенных общей идеей

BFE>>И для этого они лежат в namespace.
ЕМ>Тогда почему там не лежат члены классов?
Потому, что их объединение производится через class

ЕМ>>>Почему в обсуждаемом примере все идентификаторы начинаются на E?

BFE>>Предполагаю, что это сокращение от слова ERROR.
ЕМ>А зачем нужен такой префикс?
Чтобы показать, что всё это коды ошибок.

ЕМ>Уникальность обеспечивается и без него.

Ни с префиксом, ни без префикса уникальность не обеспечивается.

BFE>>Вот если их поместить в отдельный namespace, то они и не будут выбиваться из схемы (раз уж изначально это не было сделано)

ЕМ>Изначально, напомню, инкапсуляции не было и для полей структур. Ее тоже зря сделали?
Наоборот, делали не зря. Зря не сделали этого с enum и зря перечислениям не запретили присваивать численные значения.

BFE>>использовать #define для констант уместнее, чем enum.

ЕМ>Я бы сказал, что уместнее использовать [static] const, а при невозможности — #define. Но это для разрозненных констант, от которых не ожидается какой-либо регулярности. Если же константы хотя бы в основе завязаны на перечисление, то вполне логично определять их в перечислении.
Нет, не логично. Константы логично объединить в множество или положить в одно scope-пространство, но не в перечисление.

BFE>>Вот сейчас уже есть нормальные константы. Вы перестали использовать перечисления для того, чтобы задавать константы?

ЕМ>Я ж говорил, что для "просто констант" давно использую [static] const.
Почему не constexp ?
  Скрытый текст
#include <iostream>
#include <stdexcept>

 constexpr int next(int n) { return 1 + n; }

 constexpr int asdf = 1;
 constexpr int asdg = next(asdf);
 
int main()
{
   std::cout << '\n';
   std::cout << "asdf = " << asdf;    
   std::cout << '\n';
   std::cout << "asdg = " << asdg;    
   
   
   constexpr int arr[asdg] = {asdf, asdg};
   
   int x = 2;
   switch(x)
   {
      case arr[0]:
          std::cout << '\n' << "arr[0] ";    
      break;
      
      case arr[1]:
          std::cout << '\n' << "arr[1]";    
      break;
   }
   
}


ЕМ>Но, если мне нужны константы, например, для кодов ошибок, разделенных на группы (по степени серьезности, или по ситуациям, по связанным объектам и т.п.), то альтернативой enum будет только ручной инкремент. Вы предлагаете использовать его?

Можно и инкремент, если он вам зачем-то нужен, хотя я с трудом представляю ситуацию, где ТАКОЕ может понадобиться.
Я вообще не понимаю, зачем использовать коды ошибок. У ошибки есть имя — этого достаточно. А код — он может меняться от системы к системе, ну, как в исходном примере, с EAGAIN и EWOULDBLOCK.
Именно поэтому я для ошибок часто использую перечисления и никогда константы. И перечисления я использую именно как перечисления, то есть набор имен и никак не значений.

ЕМ>Если Вы до сих пор пытаетесь провести аналогию с шаблонными трюками, то это ж несопоставимые вещи.

Для меня это то же самое — использование конструкции языка не по назначению.

ЕМ>enum — простая конструкция, ее смысл очевиден любому, кто хоть более-менее знаком с языком. Она не порождает отдаленных последствий.

Да ну? Любому? Да 90% вообще не понимают (включая авторов стандарта), что такое перечисление и вы, похоже, из их числа, раз путаете перечисление с набором констант.

И вообще, попробуйте поспрашивать коллег, что выведет следующий код:
#include <iostream>
enum AB
 {
    a = 1,  
    b = 0xF0000000
};

enum CD
 {
    cd_c = 1,  
    cd_d = 0xF00000000
};
 
 
int main()
{
  if ( sizeof(a) == sizeof(cd_c) )
     std::cout << "yes";
  else     
     std::cout << "no";
}



ЕМ> Ну и то, что в языке таки появился enum class, уже говорит о том, что проблему осознали, и приняли меры для ее устранения.

Нет, не осознали. Даже наоборот: они ввели std::byte.

ЕМ> А что появилось в языке для замены шаблонной магии?

auto

ЕМ> Даже if constexpr можно применить только к исполняемому коду. Это не метапрограммирование, а издевательство.

Метапрограммирование ещё только предлагают, но какое-то некрасивое.
И каждый день — без права на ошибку...
Re[21]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 31.07.24 19:53
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>И вообще, попробуйте поспрашивать коллег, что выведет следующий код:

BFE>
BFE>#include <iostream>
BFE>enum AB
BFE> {
BFE>    a = 1,  
BFE>    b = 0xF0000000
BFE>};

BFE>enum CD
BFE> {
BFE>    cd_c = 1,  
BFE>    cd_d = 0xF00000000
BFE>};
 
 
BFE>int main()
BFE>{
BFE>  if ( sizeof(a) == sizeof(cd_c) )
BFE>     std::cout << "yes";
BFE>  else     
BFE>     std::cout << "no";
BFE>}    
BFE>

Да C++ он такой
enum E1 { e1_c1=1 };
enum E2 { e2_c1=1 };

void fn(enum E1 e) { std::cout<<"fn.E1\n"; }
void fn(enum E2 e) { std::cout<<"fn.E2\n"; }
void fn(char e) { std::cout<<"fn.char\n"; }
void fn(signed char e) { std::cout<<"fn.schar\n"; }
void fn(unsigned char e) { std::cout<<"fn.uchar\n"; }
void fn(short e) { std::cout<<"fn.short\n"; }
void fn(int e) { std::cout<<"fn.int\n"; }
void fn(unsigned e) { std::cout<<"fn.uint\n"; }
void fn(long long e) { std::cout<<"fn.llong\n"; }

void test() {
    fn(e1_c1);
    fn(e2_c1);
    fn('\x01');
    fn(+'\x01');
    fn(1);
    fn((char)1);
    fn((unsigned char)1);
    fn((signed char)1);
    fn((short)1);
    fn(1u);
    fn(1LL);
    fn(+e1_c1);
    fn(+e2_c1);
}

fn.E1
fn.E2
fn.char
fn.int
fn.int
fn.char
fn.uchar
fn.schar
fn.short
fn.uint
fn.llong
fn.int
fn.int

Ему мало значения константы, ему надо еще и мозг по выносить.
Отредактировано 31.07.2024 19:58 kov_serg . Предыдущая версия .
Re[21]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 01.08.24 00:26
Оценка:
Здравствуйте, B0FEE664, Вы писали:

ЕМ>>Тогда почему там не лежат члены классов?


BFE>Потому, что их объединение производится через class


Чем это принципиально отличается от объединения через enum?

ЕМ>>А зачем нужен такой префикс?


BFE>Чтобы показать, что всё это коды ошибок.


На букву E могут начинаться не только идентификаторы кодов ошибок. А если полагать, что идентификатор в верхнем регистре, начинающийся на E — это код ошибки, то что это, если не обособленная группа?

ЕМ>>Уникальность обеспечивается и без него.


BFE>Ни с префиксом, ни без префикса уникальность не обеспечивается.


Я про "локальную уникальность" среди кодов ошибок вида Exxx. Будь enum изначально инкапсулирующий, можно было сразу определить вроде enum ErrorCode { ... };

BFE>зря перечислениям не запретили присваивать численные значения.


Какие неудобства или риски это создает?

Ситуация, кстати, похожа на ту самую подстановку пакета параметров шаблона. Сейчас это возможно только в регулярном, систематическом виде. Если вдруг нужен пакет разнотипных параметров, то или никак, или через вырвиглазные трюки. А будь внутри шаблона возможность работать параметрами по отдельности, так и систематически, это можно было бы применить к более широкому спектру задач.

Так и с enum: его можно применить для создания только систематических, регулярных перечислений, а можно в эту регулярность при необходимости вмешаться. А если б вмешиваться можно было по-разному, объясняя компилятору, как сочетать регулярность с отступлениями от нее, не приходилось бы городить костыли.

BFE>Константы логично объединить в множество или положить в одно scope-пространство, но не в перечисление.


А если во множестве несколько сотен констант, 95% из которых идут подряд, но 5% этот порядок нарушают?

ЕМ>>Я ж говорил, что для "просто констант" давно использую [static] const.

BFE>Почему не constexp ?

Потому, что после VS 2008 я еще не видел столь же удобной и быстрой в работе версии, а компиляторы VS 2008 constexpr еще не знают.

ЕМ>>Но, если мне нужны константы, например, для кодов ошибок, разделенных на группы (по степени серьезности, или по ситуациям, по связанным объектам и т.п.), то альтернативой enum будет только ручной инкремент. Вы предлагаете использовать его?


BFE>Можно и инкремент, если он вам зачем-то нужен, хотя я с трудом представляю ситуацию, где ТАКОЕ может понадобиться.


Ну вот, например, коды оконных сообщений в винде в целом идут подряд, но разбиты на группы.

BFE>Я вообще не понимаю, зачем использовать коды ошибок. У ошибки есть имя — этого достаточно.


У ошибки также могут быть и свойства — уровень серьезности, область возникновения и т.п. Например, в виндовом ядре и Win32 32-разрядный код ошибки содержит несколько полей.

BFE>А код — он может меняться от системы к системе, ну, как в исходном примере, с EAGAIN и EWOULDBLOCK.


Да, и это создает приличный геморрой в линуксах, как и необходимость во многих случаях собирать софт под целевую систему. А в винде коды не меняются, они всегда одни и те же в любой системе на базе Win32. И приложение, правильно сделанное под Win95, будет в неизменном виде работать под Win11. На мой взгляд, это более серьезная степень совместимости, нежели на уровне исходников.

BFE>Именно поэтому я для ошибок часто использую перечисления и никогда константы. И перечисления я использую именно как перечисления, то есть набор имен и никак не значений.


В идеальном перечислении было бы удобно иметь возможность задавать правила присвоения значений константам. Тогда можно было бы выбирать как простое перечисление, так и более сложное, но все равно систематическое. То же самое можно было бы сделать, будь в языке макропроцессор с псевдоциклами. Без этого какие-то варианты можно реализовать только шаблонными трюками.

BFE>Для меня это то же самое — использование конструкции языка не по назначению.


Что значит "не по назначению"? Для enum изначально была заявлена возможность явного задания значений, а возможность шаблонных трюков была открыта случайно.

BFE>Да 90% вообще не понимают (включая авторов стандарта), что такое перечисление и вы, похоже, из их числа, раз путаете перечисление с набором констант.


И что же такое перечисление? А главное — из чего следует именно такая его природа, а не иная?

BFE>
BFE>    cd_d = 0xF00000000
BFE>


Как раз тот случай, когда максимальный уровень предупреждений чертовски полезен.

BFE>они ввели std::byte.


Ну, не слишком удачная попытка разгрузить char. Так-то, есть действительно что-то неправильное в том, что строковый тип смешан с числовым. По-хорошему, еще в C имело смысл сделать отдельно тип byte, и отдельно — char.

ЕМ>> А что появилось в языке для замены шаблонной магии?


BFE>auto


Что, прям всей?

BFE>Метапрограммирование ещё только предлагают, но какое-то некрасивое.


Какое метапрограммирование Вы сочли бы красивым?
Re[20]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 01.08.24 05:00
Оценка:
Здравствуйте, kov_serg, Вы писали:

S>>А речь точно идет о C++?


S>>https://wandbox.org/permlink/RWneePV7jikXCdQy


_>А теперь представьте что константы надо объявить в header-е


Пару десятков лет так делаю и полет (для примитивных типов) нормальный.

_>
_>extern const int a;
_>


А не надо в C++ тянуть привычки из чистого Си.

_>И всё приплыли.


Отучаемся говорить за всех (c)

До появления constexpr и inline const были проблемы с декларацией в хидерах констант сложных типов, вроде std::string или std::vector. Но это уже совсем другая история.

_>А enum-ы можно объявлять где попало и они constexpr.


До C++11 enum-ы были единственным нормальным способом объявлять константы в метапрограммировании, т.к. в C++98 можно было так:
template<class T>
struct some_metafunc {
  enum { value = 1 };
};

но нельзя было вот так:
template<class T>
struct some_metafunc {
  static const bool value = true;
};
Re[21]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 01.08.24 08:48
Оценка:
Здравствуйте, so5team, Вы писали:

S>До C++11 enum-ы были единственным нормальным способом объявлять константы в метапрограммировании, т.к. в C++98 можно было так:

S>
S>template<class T>
S>struct some_metafunc {
S>  enum { value = 1 };
S>};
S>

S>но нельзя было вот так:
S>
S>template<class T>
S>struct some_metafunc {
S>  static const bool value = true;
S>};
S>


Так он и остаётся единственным нормальным способом
Re[21]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 01.08.24 08:57
Оценка:
Здравствуйте, so5team, Вы писали:

S>До появления constexpr и inline const были проблемы с декларацией в хидерах констант сложных типов, вроде std::string или std::vector. Но это уже совсем другая история.

Мне нафиг не нужны сложные типы. Мне просто числовые константы нужны. Например хеш коды строк

Лучше бы сделали возможноcть явно указывать какие модули будут использоваться и их надо линковать, и указывать как статически или динамически, разместив их в отдельном файле.

Image::register_plugin(jpg);
Image::register_plugin(png);
Image::register_plugin(gif);
Image::register_plugin(webm);

...

auto image=Image::load(filename);
Re[22]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 01.08.24 09:04
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Так он и остаётся единственным нормальным способом


Общение с вами заставило вспомнить народную мудрость: не тронь говно, вонять не будет.
Re[23]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 01.08.24 09:10
Оценка:
Здравствуйте, so5team, Вы писали:

S>Общение с вами заставило вспомнить народную мудрость: не тронь говно, вонять не будет.


Лучше бы вспомнили: Будьте проще и работает — не трогай
Re[24]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 01.08.24 09:17
Оценка:
Здравствуйте, kov_serg, Вы писали:

S>>Общение с вами заставило вспомнить народную мудрость: не тронь говно, вонять не будет.


_>Лучше бы вспомнили: Будьте проще и работает — не трогай


В случае простых enum-ов оно не работает для типов, отличных от int.

В случае enum class оно не работает для типов, отличных от целочисленных, да еще и требует указывать дополнительный скоуп.

В общем, оно не работает. А я опять вляпался в известную субстанцию в попытках объяснить персонажам вроде вас очевидные вещи
Re[22]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 01.08.24 10:44
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>Тогда почему там не лежат члены классов?

BFE>>Потому, что их объединение производится через class
ЕМ>Чем это принципиально отличается от объединения через enum?
Ничем. Элементы перечисления тоже не должны были бы видимы прямо из глобального пространства.

ЕМ>>>А зачем нужен такой префикс?

BFE>>Чтобы показать, что всё это коды ошибок.
ЕМ>На букву E могут начинаться не только идентификаторы кодов ошибок. А если полагать, что идентификатор в верхнем регистре, начинающийся на E — это код ошибки, то что это, если не обособленная группа?
Её обособленность никак не выражена средствами языка.

ЕМ>>>Уникальность обеспечивается и без него.

BFE>>Ни с префиксом, ни без префикса уникальность не обеспечивается.
ЕМ>Я про "локальную уникальность" среди кодов ошибок вида Exxx. Будь enum изначально инкапсулирующий, можно было сразу определить вроде enum ErrorCode { ... };
Ну было бы пересечение по имени ErrorCode

BFE>>зря перечислениям не запретили присваивать численные значения.

ЕМ>Какие неудобства или риски это создает?
Я их не раз уже перечислял. Повторю. Добавление нового элемента в середину перечисления приводит к просмотру и правке всего кода, который прямо или косвенно связан с константами. Изменение значения элемента перечисления приводит просмотру и правке всего кода, который прямо или косвенно связан с перечислением.

ЕМ>Ситуация, кстати, похожа на ту самую подстановку пакета параметров шаблона. Сейчас это возможно только в регулярном, систематическом виде. Если вдруг нужен пакет разнотипных параметров, то или никак, или через вырвиглазные трюки.

Ерунду пишите. Нет тут ничего вырвиглазного:
    A oA{ .m_str = "55"};
    std::vector<char> vData;
    vData.push_back('5');
    vData.push_back('5');

    std::cout << oA.one_of("asdf", 5, 555, 5., vData) << std::endl;

  Скрытый текст
#include <iostream>
#include <vector>
#include <optional>
#include <variant>


class A
{
  public:
    std::string m_str;    

    bool one_of(auto... args) const
    {
        return ((*this == args) || ...);
    }

};

inline bool operator == (const A& a, const char* strC)
{
    return a.m_str == std::string(strC);
}


inline bool operator == (const A& a, int n)
{
    return a.m_str == std::to_string(n);
}


inline bool operator == (const A& a, float f)
{
    return a.m_str == std::to_string(f);
}


inline bool operator == (const A& a, double f)
{
    return a.m_str == std::to_string(f);
}


inline bool operator == (const A& a, const std::vector<char>& v)
{
    return a.m_str == std::string(v.begin(), v.end());
}


int main()
{
    A oA{ .m_str = "55"};

    std::vector<char> vData;
    vData.push_back('5');
    vData.push_back('5');

    std::cout << oA.one_of("asdf", 5, 555, 5., vData) << std::endl;

    return 0;
}

здесь


ЕМ>А будь внутри шаблона возможность работать параметрами по отдельности, так и систематически, это можно было бы применить к более широкому спектру задач.

Сомневаюсь, что такое вообще возможно без рантайм информации о типе. Убеждён, что если же вам нужна рантайм информации о типе, значит вы что-то не понимаете в программировании на C++.

ЕМ>Так и с enum: его можно применить для создания только систематических, регулярных перечислений, а можно в эту регулярность при необходимости вмешаться. А если б вмешиваться можно было по-разному, объясняя компилятору, как сочетать регулярность с отступлениями от нее, не приходилось бы городить костыли.

То, что вы понимаете под регулярностью, я считаю вообще не должно быть в языке и является вредным излишеством.

BFE>>Константы логично объединить в множество или положить в одно scope-пространство, но не в перечисление.

ЕМ>А если во множестве несколько сотен констант, 95% из которых идут подряд, но 5% этот порядок нарушают?
Всего несколько сотен? Вот реально мне не сложно было бы их прописать так, как я уже это делал выше:
constexpr int next(int n) { return 1 + n; }

constexpr int a = 1;
constexpr int b = next(a);
constexpr int c = next(b);
constexpr int d = c + 123;
constexpr int e = next(d);


ЕМ>>>Я ж говорил, что для "просто констант" давно использую [static] const.

BFE>>Почему не constexp ?
ЕМ>Потому, что после VS 2008 я еще не видел столь же удобной и быстрой в работе версии, а компиляторы VS 2008 constexpr еще не знают.
Если мне не изменяет память VS 2008 можно сконфигурировать с новыми компиляторами.

ЕМ>>>Но, если мне нужны константы, например, для кодов ошибок, разделенных на группы (по степени серьезности, или по ситуациям, по связанным объектам и т.п.), то альтернативой enum будет только ручной инкремент. Вы предлагаете использовать его?

Нет, я предлагаю вообще не полагаться на численные значения для ошибок.
Но если вам они зачем-то, таки, нужны, то — да, только ручной инкремент, как в примере выше.

BFE>>Можно и инкремент, если он вам зачем-то нужен, хотя я с трудом представляю ситуацию, где ТАКОЕ может понадобиться.

ЕМ>Ну вот, например, коды оконных сообщений в винде в целом идут подряд, но разбиты на группы.
И зачем закладываться на их числовые значения? Я не уверен, кстати, что кто-то так делает...

BFE>>Я вообще не понимаю, зачем использовать коды ошибок. У ошибки есть имя — этого достаточно.

ЕМ>У ошибки также могут быть и свойства — уровень серьезности, область возникновения и т.п.
Может, конечно. Только для определения уровень серьезности, область возникновения и т.п. числовое значение не обязательно.

ЕМ>Например, в виндовом ядре и Win32 32-разрядный код ошибки содержит несколько полей.

Да, но его текстовое описание, например, всё равно лежит отдельно.

BFE>>А код — он может меняться от системы к системе, ну, как в исходном примере, с EAGAIN и EWOULDBLOCK.

ЕМ>Да, и это создает приличный геморрой в линуксах, как и необходимость во многих случаях собирать софт под целевую систему.
Обычно, если читать документацию и думать головой — то никаких проблем с этим нет.

ЕМ>А в винде коды не меняются, они всегда одни и те же в любой системе на базе Win32.

А вот из-за этого, да — неприличный "геморрой". Пример ниже.

ЕМ>И приложение, правильно сделанное под Win95, будет в неизменном виде работать под Win11. На мой взгляд, это более серьезная степень совместимости, нежели на уровне исходников.

Не будут. Стоит только указать путь до файла достаточно длинный, как всё поломается. Причём чаше всего ломается достаточно быстро, 261 символ и всё — приплыли. А почему? Из-за неизменной константы.

BFE>>Именно поэтому я для ошибок часто использую перечисления и никогда константы. И перечисления я использую именно как перечисления, то есть набор имен и никак не значений.

ЕМ>В идеальном перечислении было бы удобно иметь возможность задавать правила присвоения значений константам. Тогда можно было бы выбирать как простое перечисление, так и более сложное, но все равно систематическое. То же самое можно было бы сделать, будь в языке макропроцессор с псевдоциклами. Без этого какие-то варианты можно реализовать только шаблонными трюками.
Ничего подобного. Перечисление — оно на то и перечисление, что само значений не хранит. А вот автоматическое сопоставление различных значений различных типов элементам перечислений было бы удобно и его реально не хватает.

BFE>>Для меня это то же самое — использование конструкции языка не по назначению.

ЕМ>Что значит "не по назначению"? Для enum изначально была заявлена возможность явного задания значений, а возможность шаблонных трюков была открыта случайно.
Значит, что в нормальных перечислениях одному элементу перечисления может быть сопоставлено несколько разных значений. А то, что в С++ в enum изначально была заявлена возможность явного задания значений — это от бедности и концептуально ошибочно.

BFE>>Да 90% вообще не понимают (включая авторов стандарта), что такое перечисление и вы, похоже, из их числа, раз путаете перечисление с набором констант.

ЕМ>И что же такое перечисление? А главное — из чего следует именно такая его природа, а не иная?
Перечисление — это то, что можно перечислить.

Перечисляемый тип (сокращённо перечисле́ние, англ. enumeration, enumerated type) — в программировании тип данных, чьё множество значений представляет собой ограниченный список идентификаторов.

Элементу перечисления можно сопоставить некоторые значения и сделать это можно различными способами. Другое дело, что делать сопоставление руками чревато ошибками.

BFE>>
BFE>>    cd_d = 0xF00000000
BFE>>

ЕМ>Как раз тот случай, когда максимальный уровень предупреждений чертовски полезен.
Это как раз тот случай, когда изменение значения enum порождает отдалённые последствия.

ЕМ>>> А что появилось в языке для замены шаблонной магии?

BFE>>auto
ЕМ>Что, прям всей?

Если вам не нужна шаблонная магия, то не используйте её.

BFE>>Метапрограммирование ещё только предлагают, но какое-то некрасивое.

ЕМ>Какое метапрограммирование Вы сочли бы красивым?
Это сложный вопрос, я не готов на него ответить.
И каждый день — без права на ошибку...
Re[23]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 01.08.24 12:21
Оценка: :)
Здравствуйте, B0FEE664, Вы писали:

BFE>Элементы перечисления тоже не должны были бы видимы прямо из глобального пространства.


Что-то я запутался. Когда я Вам приводил enum class в качестве примера разумного прогресса в языке, Вы ответили в том смысле, что "не очень-то и хотелось", поскольку enum можно было завернуть в namespace.

BFE>Её обособленность никак не выражена средствами языка.


Ну так и я о том же. Изначально (еще с C) ущербная реализация enum породила эту порочную практику с префиксами. Возможно, на такую реализацию пошли сознательно, чтоб предельно упростить компилятор. Но потом-то, когда добавили инкапсуляцию полей структур, было самое время добавить ее и к enum, предусмотрев режим обратной совместимости для извращенцев и ретроградов. Но предпочли тянуть аж двадцать лет, куда это годится?

BFE>Ну было бы пересечение по имени ErrorCode


И "увидели бы они, что это хорошо".

BFE>Добавление нового элемента в середину перечисления приводит к просмотру и правке всего кода, который прямо или косвенно связан с константами. Изменение значения элемента перечисления приводит просмотру и правке всего кода, который прямо или косвенно связан с перечислением.


И Вас не смущает, что код, требующий такого пересмотра, фундаментально ущербен? Если код определяет "просто перечисление", то он не имеет оснований закладываться ни на количество элементов, ни на их значения — только на то, что они будут уникальными в пределах перечисления, и возрастать монотонно. Вы еще посетуйте, что код, закладывающийся на порядок следования данных-членов класса, их смещения и размеры, ломается при вставке новых членов в середину.

Если код таки закладывается на значения элементов и/или соотношения между ними, то нужно или принимать меры, которые я упоминал выше (например, добавлять элементы вида Group1_First, Group1_Last, Group2_First, Group2_Last и т.п.), или определять эти значения явно (против чего вы почему-то активно выступаете). На самый худющий конец — обернуть этот enum огромнейшим комментарием, обещающим ужасные кары тому, кто осмелится его менять неподобающим образом, и вдобавок наставить assert'ов, проверяющих соотношения.

ЕМ>>Если вдруг нужен пакет разнотипных параметров, то или никак, или через вырвиглазные трюки.


BFE>Ерунду пишите. Нет тут ничего вырвиглазного:


А сможете этот пакет разобрать поштучно непосредственно в шаблоне, чтобы, в зависимости от типов, породить произвольный код, и чтоб это не выглядело похожим на традиционное "char* (*(*foo[5])(char*))[];", которым только детей пугать?

BFE>Сомневаюсь, что такое вообще возможно без рантайм информации о типе.


Зачем здесь рантайм? Я говорю исключительно о времени компиляции. Чтоб в шаблоне можно было как обращаться к его параметрам по индексам, так и записать конструкцию псевдоцикла, перебирающего параметры, узнающего у компилятора свойства фактических параметров, чтоб в итоге порождать код в зависимости от любых необходимых условий.

То есть, чтоб шаблон работал подобно обычной функции, работающей со своими фактическими параметрами через имена формальных, но вместо выполнения операторов порождал код для последующей компиляции, как это делает любой адекватный макропроцессор.

BFE>Убеждён, что если же вам нужна рантайм информации о типе, значит вы что-то не понимаете в программировании на C++.


А если я что-то понимаю, но информация мне все равно нужна? Например, в сугубо отладочных целях?

BFE>То, что вы понимаете под регулярностью, я считаю вообще не должно быть в языке и является вредным излишеством.


Я опять запутался. Решения, которые Вы делаете с помощью пакетов параметров, основаны на этой самой регулярности: Вы указываете только базовую схему подстановки, а компилятор разворачивает ее, единообразно подставляя каждый параметр пакета. Или Вы этого тоже не одобряете, и используете лишь от безысходности?

Из Вашего утверждения также следует, что массивы в языке тоже являются вредным излишеством. Ну, или Вы меня неправильно поняли.

BFE>мне не сложно было бы их прописать так, как я уже это делал выше:

BFE>
BFE>constexpr int b = next(a);
BFE>constexpr int c = next(b);
BFE>


Во-первых, даже для десятка-другого это будет чрезмерно громоздко (а следовательно, менее понятно и наглядно) по сравнению с enum. Во-вторых, большинство имен придется указывать в двух местах, а это влечет за собой известную проблему согласованности, но средств автоматизированного обеспечения согласованности у нас нет. Я не вижу в таком способе ни одного преимущества перед enum, но вижу серьезные недостатки.

BFE>Если мне не изменяет память VS 2008 можно сконфигурировать с новыми компиляторами.


Штатно — нельзя. У нее все свойства платформы забиты в двоичные файлы конфигурации, их только хачить. Ну, или делать самодельные переходники для cl.exe/link.exe, которые будут запускать новые версии, попутно правя командные строки, поскольку в VS 2005/2008 некоторые ключи вставляются всегда, а новые компиляторы на них ругаются.

BFE>я предлагаю вообще не полагаться на численные значения для ошибок.


В смысле, на конкретные численные значения? Для этого достаточно даже простого перечисления имен в enum, а если нам нужно знать о них что-то большее, то конкретные значения появляются только в одном месте, во всех остальных используются только имена. То есть, на конкретные значения никто не полагается — в точке определения их можно менять, и нигде ничего не поломается.

BFE>Но если вам они зачем-то, таки, нужны, то — да, только ручной инкремент


Ей-богу, не понимаю, почему "только ручной", когда язык предоставляет автоматический.

ЕМ>>например, коды оконных сообщений в винде в целом идут подряд, но разбиты на группы.


BFE>И зачем закладываться на их числовые значения? Я не уверен, кстати, что кто-то так делает...


Никто и не делает — все пользуются именами. Имена, кстати, определены литерально — через #define, чтоб гарантировать одинаковые значения во всех системах. Но для себя я не вижу причин отказываться от enum, ибо это его изначальная, штатная функция.

BFE>для определения уровень серьезности, область возникновения и т.п. числовое значение не обязательно.


Если Вы о том, чтобы завернуть все это в объект, то такое не прокатит в глобальном масштабе. Если ОС будет предоставлять интерфейс на продвинутом C++, разработчики других языков замучатся их сопрягать. Они и с традиционным сишным не всегда могут сопрячь.

А у себя-то, в пределах своего продукта, вполне себе можно.

ЕМ>>Например, в виндовом ядре и Win32 32-разрядный код ошибки содержит несколько полей.

BFE>Да, но его текстовое описание, например, всё равно лежит отдельно.

Да, это недостаток. Но, увы, пока неустранимый.

ЕМ>>И приложение, правильно сделанное под Win95, будет в неизменном виде работать под Win11.


BFE>Не будут. Стоит только указать путь до файла достаточно длинный, как всё поломается. Причём чаше всего ломается достаточно быстро, 261 символ и всё — приплыли.


На практике, пути длиннее 260 символов возникают достаточно редко. У меня, например, ни одного нет.

BFE>Из-за неизменной константы.


Так это системная константа, а не собственная в приложении. То есть, это одна из характеристик ОС, под которую делалось приложений. В линуксах ведь тоже все лезут напрямую в /bin, /usr/local/share, а не спрашивают систему о конкретных путях "именно здесь".

BFE>Перечисление — оно на то и перечисление, что само значений не хранит.


Оно не может не хранить значений, иначе его не реализовать. Вы хотели сказать — не отдает в явном виде?

BFE>А вот автоматическое сопоставление различных значений различных типов элементам перечислений было бы удобно и его реально не хватает.


Поддерживаю. Но лучше бы в качестве расширения, а не замены того, что есть.

BFE>Значит, что в нормальных перечислениях одному элементу перечисления может быть сопоставлено несколько разных значений.


Это уже не простое перечисление, а некий агрегат. Такое тоже полезно, но лишь в качестве дополнения к простому. В большом количестве задач фигурируют именно простые перечислимые значения, зачем искусственно усложнять?

BFE>А то, что в С++ в enum изначально была заявлена возможность явного задания значений — это от бедности и концептуально ошибочно.


Не от бедности, а от простоты и эффективности. Напомню, что уникальность C++ именно в том, чтобы иметь возможность работать сколь угодно близко к аппаратуре. Для абстракций наделали более других языков, но близости к аппаратуре больше нет нигде. И в C++ абстракции имеет смысл добавлять на абы как, а чтоб не поломать исходную идею "языка произвольного уровня".

BFE>Перечисление — это то, что можно перечислить.


Верно, но это не просто абстрактное множество, а упорядоченное.

BFE>Элементу перечисления можно сопоставить некоторые значения и сделать это можно различными способами. Другое дело, что делать сопоставление руками чревато ошибками.


Ваш явный способ через next ими еще более чреват.

BFE>Это как раз тот случай, когда изменение значения enum порождает отдалённые последствия.


Не более отдаленные, чем изменение значения любой числовой константы. Без них не обойтись, когда код не самодостаточен, а самодостаточного кода гораздо меньше, чем системно-зависимого.

BFE>Если вам не нужна шаблонная магия, то не используйте её.


Так я и не использую. А вот некоторые решения, которые более-менее компактно делаются только с ее помощью, мне как раз бывают нужны. Приходится городить традиционными способами.

ЕМ>>Какое метапрограммирование Вы сочли бы красивым?


BFE>Это сложный вопрос, я не готов на него ответить.


На мой взгляд, для красивого метапрограммирования необходимы средства как функциональной записи, давно продвигаемые в стандарт языка, так и процедурной, которой почему-то избегают любой ценой, будто она заразна.
Re[24]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 01.08.24 13:05
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Чтоб в шаблоне можно было как обращаться к его параметрам по индексам


Это уже грядет: https://en.cppreference.com/w/cpp/language/pack_indexing

ЕМ>Не от бедности, а от простоты и эффективности. Напомню, что уникальность C++ именно в том, чтобы иметь возможность работать сколь угодно близко к аппаратуре.


Наверное именно поэтому в C++ легально напрямую сравнивать (на больше/меньше) указатели только в случае, если они ссылаются на элементы одного и того же массива. И нужны костыли в виде std::launder и std::start_lifetime_as, потому что компилятор воображает себе невесть что. Размерности типов char, short, int, long в стандарте не зафиксированы, типы std::intN_t являются опциональными, а точная размерность типов int_fastN_t/int_leastN_t не определена. А пустой бесконечный цикл в main в текущих стандартах рассматривается как UB.

Сколь угодная близость к аппаратуре она же именно такая, да?
Re[25]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 01.08.24 13:18
Оценка:
Здравствуйте, so5team, Вы писали:

S>В случае простых enum-ов оно не работает для типов, отличных от int.

S>В случае enum class оно не работает для типов, отличных от целочисленных, да еще и требует указывать дополнительный скоуп.
S>В общем, оно не работает. А я опять вляпался в известную субстанцию в попытках объяснить персонажам вроде вас очевидные вещи

Всё работает просто вы не так используете. В C++ принято всё через жопу делать
#include <iostream>
using namespace std;

enum E {
    name1,
    name2,
    name3
};

template <template<enum E> class M>
static auto E_get_value(enum E e) {
    switch(e) {
        case name1: return M<name1>::value();
        case name2: return M<name2>::value();
        case name3: return M<name3>::value();
    }
    throw;
}

template <template<enum E> class... M>
static auto E_forall(auto fn) {
    fn(M<name1>::value()...);
    fn(M<name2>::value()...);
    fn(M<name3>::value()...);
}

template<enum E e>const char* Es();
template<>const char* Es<name1>() { return "name1"; }
template<>const char* Es<name2>() { return "name2"; }
template<>const char* Es<name3>() { return "name3"; }

template<enum E e>double Ed();
template<>double Ed<name1>() { return 1; }
template<>double Ed<name2>() { return 0.5; }
template<>double Ed<name3>() { return 3.14; }

template<enum E e>struct Ed_values;
template<>struct Ed_values<name1> { static double value() { return Ed<name1>(); }};
template<>struct Ed_values<name2> { static double value() { return Ed<name2>(); }};
template<>struct Ed_values<name3> { static double value() { return Ed<name3>(); }};

template<enum E e>struct Es_values;
template<>struct Es_values<name1> { static const char* value() { return Es<name1>(); }};
template<>struct Es_values<name2> { static const char* value() { return Es<name2>(); }};
template<>struct Es_values<name3> { static const char* value() { return Es<name3>(); }};

template<enum E e>struct Eb;
template<>struct Eb<name1> {
    template<class T>
    static void run(T t) { t(); }
};
template<>struct Eb<name2> {
    template<class T>
    static void run(T t) { try { t(); } catch(...) {} }
};
template<>struct Eb<name3> {
    template<class T>
    static void run(T t) { try { t(); } catch(...) { cerr<<"catch\n"; } }
};

int main(int argc, char const *argv[]) {
    // не целые константы
    cout<<(Es<name3>())<<"="<<(Ed<name3>())<<endl;
    // получение значение
    enum E e=name2; cout<<E_get_value<Ed_values>(e)<<endl;
    // перебираем все значения
    E_forall<Es_values,Ed_values>([](const char* name,double value){
        cout<<name<<"="<<value<<endl;
    });
    // выбираем алгоритм по имени
    Eb<name3>::run([]{ throw 0; });
    return 0;
}

name3=3.14
0.5
name1=1
name2=0.5
name3=3.14
catch
Отредактировано 01.08.2024 14:16 kov_serg . Предыдущая версия . Еще …
Отредактировано 01.08.2024 13:22 kov_serg . Предыдущая версия .
Re[25]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 01.08.24 14:03
Оценка:
Здравствуйте, so5team, Вы писали:

S>Наверное именно поэтому в C++ легально напрямую сравнивать (на больше/меньше) указатели только в случае, если они ссылаются на элементы одного и того же массива. И нужны костыли в виде std::launder и std::start_lifetime_as, потому что компилятор воображает себе невесть что. Размерности типов char, short, int, long в стандарте не зафиксированы, типы std::intN_t являются опциональными, а точная размерность типов int_fastN_t/int_leastN_t не определена. А пустой бесконечный цикл в main в текущих стандартах рассматривается как UB.


S>Сколь угодная близость к аппаратуре она же именно такая, да?

Это называется over-engineering. Когда хочется что бы было очень научно. В результате из-за несогласованности в основании эта научность выливается в стандартизацию костылей.
Re[25]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 01.08.24 14:16
Оценка:
Здравствуйте, so5team, Вы писали:

S>Это уже грядет: https://en.cppreference.com/w/cpp/language/pack_indexing


Что грядет — хорошо. Но то, что грядет с опозданием на десятки лет — плохо. Ведь все это в разных формах было придумано и использовалось давным-давно, но многие ли знают историю до момента своего вхождения в профессию?

S>Наверное именно поэтому в C++ легально напрямую сравнивать (на больше/меньше) указатели только в случае, если они ссылаются на элементы одного и того же массива. И нужны костыли в виде std::launder и std::start_lifetime_as, потому что компилятор воображает себе невесть что.


В общем случае это вполне логично, поскольку разные массивы могут быть размещены в разных сегментах, и компилятор обязан это учитывать. Если на платформе плоская модель памяти, он может и не возбуждаться на эту ситуацию. Соответственно, реализации launder для этой платформы должны проверять, где находятся адреса.

S>Размерности типов char, short, int, long в стандарте не зафиксированы


Это не страшно, к размерам нетрудно привязаться через платформенно-зависимые заголовки.

S>пустой бесконечный цикл в main в текущих стандартах рассматривается как UB.


По-моему, пустой бесконечный цикл нигде не должен считаться нормой. Есть исключения?

S>Сколь угодная близость к аппаратуре она же именно такая, да?


Кое-в-чем могло быть и поближе, было бы неплохо. Главное, что это никак не мешает делать сколь угодно абстрактные программы, которым нужна предельная переносимость, а не предельная эффективность.
Re[24]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 01.08.24 14:28
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>Элементы перечисления тоже не должны были бы видимы прямо из глобального пространства.

ЕМ>Что-то я запутался. Когда я Вам приводил enum class в качестве примера разумного прогресса в языке, Вы ответили в том смысле, что "не очень-то и хотелось", поскольку enum можно было завернуть в namespace.
Нет, я ответил в том смысле, что убрать значения enum из глобального пространства — это не совсем та цель, которая ставилась при введении enum class. Основная цель — создание независимых типов.

BFE>>Ну было бы пересечение по имени ErrorCode

ЕМ>И "увидели бы они, что это хорошо".
В каком смысле "хорошо"? Вот, допустим, у вас два разных enum и оба называются ErrorCode. Что делать?

BFE>>Добавление нового элемента в середину перечисления приводит к просмотру и правке всего кода, который прямо или косвенно связан с константами. Изменение значения элемента перечисления приводит просмотру и правке всего кода, который прямо или косвенно связан с перечислением.

ЕМ>И Вас не смущает, что код, требующий такого пересмотра, фундаментально ущербен?
Вообще-то я об этом пишу с самого начала.

ЕМ>Если код определяет "просто перечисление", то он не имеет оснований закладываться ни на количество элементов, ни на их значения — только на то, что они будут уникальными в пределах перечисления, и возрастать монотонно.

Да.

ЕМ> Вы еще посетуйте, что код, закладывающийся на порядок следования данных-членов класса, их смещения и размеры, ломается при вставке новых членов в середину.

Код, который закладывается на порядок следования данных-членов класса встречается намного реже, чем enum как константы.

ЕМ>Если код таки закладывается на значения элементов и/или соотношения между ними, то нужно или принимать меры, которые я упоминал выше (например, добавлять элементы вида Group1_First, Group1_Last, Group2_First, Group2_Last и т.п.), или определять эти значения явно (против чего вы почему-то активно выступаете). На самый худющий конец — обернуть этот enum огромнейшим комментарием, обещающим ужасные кары тому, кто осмелится его менять неподобающим образом, и вдобавок наставить assert'ов, проверяющих соотношения.

А можно просто не использовать enum как константы.

ЕМ>>>Если вдруг нужен пакет разнотипных параметров, то или никак, или через вырвиглазные трюки.

BFE>>Ерунду пишите. Нет тут ничего вырвиглазного:

ЕМ>А сможете этот пакет разобрать поштучно непосредственно в шаблоне, чтобы, в зависимости от типов, породить произвольный код, и чтоб это не выглядело похожим на традиционное "char* (*(*foo[5])(char*))[];", которым только детей пугать?

Что ещё за "произвольный" код? В зависимости от типа в примере и так вызывается разный код.

BFE>>Сомневаюсь, что такое вообще возможно без рантайм информации о типе.

ЕМ>Зачем здесь рантайм? Я говорю исключительно о времени компиляции. Чтоб в шаблоне можно было как обращаться к его параметрам по индексам, так и записать конструкцию псевдоцикла, перебирающего параметры, узнающего у компилятора свойства фактических параметров, чтоб в итоге порождать код в зависимости от любых необходимых условий.
Возьмите кортеж и будет там обращение по индексам. Но зачем?
Что такое псевдоцикл?
Порождать код? Т.е. вы хотите автоматическую кодогенерацию? Метапрограммирование? Это интересная возможность и было бы не плохо её добавить в C++, пока что в планах у комитета вот такое: p2996r4

ЕМ>То есть, чтоб шаблон работал подобно обычной функции, работающей со своими фактическими параметрами через имена формальных, но вместо выполнения операторов порождал код для последующей компиляции, как это делает любой адекватный макропроцессор.

Хотелки — хотелками, а "Если вдруг нужен пакет разнотипных параметров, то или никак, или через вырвиглазные трюки", то и сейчас никаких вырвиглазных трюков.

BFE>>Убеждён, что если же вам нужна рантайм информации о типе, значит вы что-то не понимаете в программировании на C++.

ЕМ>А если я что-то понимаю, но информация мне все равно нужна? Например, в сугубо отладочных целях?
Вангую: в ближайшие двадцать лет появятся отладчики для процесса компиляции.

ЕМ>Из Вашего утверждения также следует, что массивы в языке тоже являются вредным излишеством. Ну, или Вы меня неправильно поняли.

Очевидно, проблемы с взаимным пониманием.

BFE>>мне не сложно было бы их прописать так, как я уже это делал выше:

BFE>>
BFE>>constexpr int b = next(a);
BFE>>constexpr int c = next(b);
BFE>>


ЕМ>Во-первых, даже для десятка-другого это будет чрезмерно громоздко (а следовательно, менее понятно и наглядно) по сравнению с enum. Во-вторых, большинство имен придется указывать в двух местах, а это влечет за собой известную проблему согласованности, но средств автоматизированного обеспечения согласованности у нас нет. Я не вижу в таком способе ни одного преимущества перед enum, но вижу серьезные недостатки.

Не обязательно писать так примитивно, можно и разного универсального накрутить.
Вот чуть более продвинутый пример: здесь
Автор: B0FEE664
Дата: 12.04.24
. На его основе можно написать сколь угодно сложную инициализацию констант.

ЕМ>В смысле, на конкретные численные значения? Для этого достаточно даже простого перечисления имен в enum, а если нам нужно знать о них что-то большее, то конкретные значения появляются только в одном месте, во всех остальных используются только имена. То есть, на конкретные значения никто не полагается — в точке определения их можно менять, и нигде ничего не поломается.

Я рад за вас и вашу веру в "на конкретные значения никто не полагается".
У меня вопрос: как часто вы правите чужие исходники?

ЕМ>Если Вы о том, чтобы завернуть все это в объект, то такое не прокатит в глобальном масштабе. Если ОС будет предоставлять интерфейс на продвинутом C++, разработчики других языков замучатся их сопрягать. Они и с традиционным сишным не всегда могут сопрячь.

Обёртку для C++ вполне можно написать.

ЕМ>Да, это недостаток. Но, увы, пока неустранимый.

Вполне устранимый, если не на прямую с API работать.

ЕМ>На практике, пути длиннее 260 символов возникают достаточно редко. У меня, например, ни одного нет.

А я часто на это нарываюсь. Вот пример здесь
Автор: B0FEE664
Дата: 08.09.21
.

BFE>>Из-за неизменной константы.

ЕМ>Так это системная константа, а не собственная в приложении. То есть, это одна из характеристик ОС, под которую делалось приложений. В линуксах ведь тоже все лезут напрямую в /bin, /usr/local/share, а не спрашивают систему о конкретных путях "именно здесь".
Не, система-то как раз поддерживает длинные пути, а вот приложение — нет.

BFE>>Перечисление — оно на то и перечисление, что само значений не хранит.

ЕМ>Оно не может не хранить значений, иначе его не реализовать. Вы хотели сказать — не отдает в явном виде?
Может не хранить в том смысле, что они могут различаться от закуска к запуску, например.

ЕМ>Это уже не простое перечисление, а некий агрегат.

Не обязательно делать его одним объектом.

ЕМ>Такое тоже полезно, но лишь в качестве дополнения к простому. В большом количестве задач фигурируют именно простые перечислимые значения, зачем искусственно усложнять?

Почему вы пишите значения, если речь о именах?

BFE>>А то, что в С++ в enum изначально была заявлена возможность явного задания значений — это от бедности и концептуально ошибочно.

ЕМ>Не от бедности, а от простоты и эффективности. Напомню, что уникальность C++ именно в том, чтобы иметь возможность работать сколь угодно близко к аппаратуре. Для абстракций наделали более других языков, но близости к аппаратуре больше нет нигде. И в C++ абстракции имеет смысл добавлять на абы как, а чтоб не поломать исходную идею "языка произвольного уровня".
Перечисления к аппаратуре имеют весьма опосредствованное отношение.

BFE>>Перечисление — это то, что можно перечислить.

ЕМ>Верно, но это не просто абстрактное множество, а упорядоченное.
Не обязательно. Зачем упорядоченность?

BFE>>Элементу перечисления можно сопоставить некоторые значения и сделать это можно различными способами. Другое дело, что делать сопоставление руками чревато ошибками.

ЕМ>Ваш явный способ через next ими еще более чреват.
Не путайте: next — это для констант.

BFE>>Это как раз тот случай, когда изменение значения enum порождает отдалённые последствия.

ЕМ>Не более отдаленные, чем изменение значения любой числовой константы.
Меняется не просто значение, меняется тип, в который может перечисление конвертироваться.

ЕМ>Без них не обойтись, когда код не самодостаточен, а самодостаточного кода гораздо меньше, чем системно-зависимого.

Самодостаточного кода намного больше, чем системно-зависимого.

ЕМ>На мой взгляд, для красивого метапрограммирования необходимы средства как функциональной записи, давно продвигаемые в стандарт языка, так и процедурной, которой почему-то избегают любой ценой, будто она заразна.

Не избегают, а реализовать не могут из-за сложности.
И каждый день — без права на ошибку...
Re[26]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 01.08.24 16:54
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Всё работает просто вы не так используете. В C++ принято всё через жопу делать


Я не понял, вам что, нравится испражняться на публике или вы хвастаетесь собственным скудоумием?
Re[26]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 01.08.24 17:02
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

S>>Это уже грядет: https://en.cppreference.com/w/cpp/language/pack_indexing


ЕМ>Что грядет — хорошо. Но то, что грядет с опозданием на десятки лет — плохо.


А вы много лет продвигали правильный вариант, но ваше мнение игнорировали? Или много лет спонсировали комитет по стандартизации, а он упорно не принимал то, что вы хотели?

ЕМ>Ведь все это в разных формах было придумано и использовалось давным-давно


Примеры можно? Может вы про PL/1?

ЕМ>но многие ли знают историю до момента своего вхождения в профессию?


Вы и сейчас дофига не знаете. Что не мешает вам вещать пургу с умными видом и уныло сливаться как только речь заходит о конкретике.

S>>Наверное именно поэтому в C++ легально напрямую сравнивать (на больше/меньше) указатели только в случае, если они ссылаются на элементы одного и того же массива. И нужны костыли в виде std::launder и std::start_lifetime_as, потому что компилятор воображает себе невесть что.


ЕМ>В общем случае это вполне логично, поскольку разные массивы могут быть размещены в разных сегментах, и компилятор обязан это учитывать. Если на платформе плоская модель памяти, он может и не возбуждаться на эту ситуацию.


Указатель это такая абстракция в C++, которая скрывает от программиста что из себя представляет адрес на конкретной платформе. Это, блин, ну просто максимально близкий к железу уровень. Ага.

ЕМ>Соответственно, реализации launder для этой платформы должны проверять, где находятся адреса.


Может стоило бы подучить матчасть, тем более, что она появилась уже после вашего вхождения в профессию. Просто чтобы глупости не говорить. Хотя бы про launder.

ЕМ>По-моему, пустой бесконечный цикл нигде не должен считаться нормой. Есть исключения?


https://rsdn.org/forum/cpp/8600448.1
Автор: vsb
Дата: 16.09.23
Re[25]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 01.08.24 17:29
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>убрать значения enum из глобального пространства — это не совсем та цель, которая ставилась при введении enum class. Основная цель — создание независимых типов.


Почему "создание"? enum в C++ изначально представляет собой тип.

BFE>допустим, у вас два разных enum и оба называются ErrorCode. Что делать?


То же самое, если два разных класса, глобальных объекта, функции с одинаковыми именами.

ЕМ>>Вас не смущает, что код, требующий такого пересмотра, фундаментально ущербен?


BFE>Вообще-то я об этом пишу с самого начала.


Мне показалось, что Вы считаете ущербной как раз реализацию enum, а код, который от него ломается — невинной жертвой.

Я же не вижу проблем написать с помощью enum код, который как вообще не зависит от количества/порядка элементов, так и зависит от него с минимальным риском поломки. По-моему, здесь достаточно широкий спектр возможностей.

BFE>Код, который закладывается на порядок следования данных-членов класса встречается намного реже, чем enum как константы.


Это просто потому, что из порядка членов меньше возможностей извлечь практическую пользу.

BFE>А можно просто не использовать enum как константы.


Так можно и int не использовать, а на каждый числовой тип создавать свой класс с набором операций. Но где-то придется остановиться.

BFE>Что ещё за "произвольный" код? В зависимости от типа в примере и так вызывается разный код.


У Вас есть возможность подставить только вызов функции, но не любой код, синтаксически допустимый в этом месте.

BFE>Возьмите кортеж и будет там обращение по индексам. Но зачем?


Незачем, ибо кортежем можно манипулировать только во время выполнения, а я говорю про время компиляции.

BFE>Что такое псевдоцикл?


Цикл, который крутится во время компиляции, перебирая фактические параметры шаблона, элементы кортежа, или даже просто порождая копии кода (одинаковые или зависящие от итерации).

BFE>Порождать код? Т.е. вы хотите автоматическую кодогенерацию? Метапрограммирование?


Хочу.

BFE>Это интересная возможность и было бы не плохо её добавить в C++, пока что в планах у комитета вот такое


Как всегда, корявенько. Все пытаются сделать покороче, сэкономить несколько символов, хотя полные конструкции уже давно стали весьма развесистыми. Лучше б обозначать набор свойств типа по аналогии со структурой: T.type_id, T.type_name, T.is_arithmetic, T.num_of_members, T.members [2].is_function, T.members [2].args [3].type_id, и т.п.

BFE>"Если вдруг нужен пакет разнотипных параметров, то или никак, или через вырвиглазные трюки", то и сейчас никаких вырвиглазных трюков.


Это если развернуть пакет в подстановку вызовов функций, которые определены где-то отдельно. А разобрать пакет по параметрам, подставив каждый из них более другим образом, сейчас можно только через магию с рекурсией, и выглядит это ну очень вырвиглазно. А главное — чужеродно и непоследовательно. Каждое новое решение приходится втискивать в старый синтаксис, который и без того же давно перегружен.

BFE>Вангую: в ближайшие двадцать лет появятся отладчики для процесса компиляции.


По уму, должны были появиться лет двадцать пять назад, когда активно пошли в ход вложенные и рекурсивные шаблоны. На худой конец — возможность вывода отладочной информации на каждой стадии разбора/обработки/подстановки.

BFE>Не обязательно писать так примитивно, можно и разного универсального накрутить.

BFE>Вот чуть более продвинутый пример: здесь
Автор: B0FEE664
Дата: 12.04.24
. На его основе можно написать сколь угодно сложную инициализацию констант.


А можно и базу данных прикрутить...

BFE>Я рад за вас и вашу веру в "на конкретные значения никто не полагается".


Каким именно образом полагаются там, где Вы видели?

BFE>как часто вы правите чужие исходники?


Редко. Такое, чтоб использовало enum прям совсем уж коряво, попадалось редко.

BFE>Обёртку для C++ вполне можно написать.


Так и пишут. У меня самого классы для работы с кодами ошибок ОС.

ЕМ>>Да, это недостаток. Но, увы, пока неустранимый.

BFE>Вполне устранимый, если не на прямую с API работать.

ЕМ>>На практике, пути длиннее 260 символов возникают достаточно редко. У меня, например, ни одного нет.

BFE>А я часто на это нарываюсь.

Какая необходимость заставляет использовать софт из-под Win9x с непременно в дереве каталогов, у которого одновременно и много уровней, и длинные имена?

Если у GCC это ограничение сохранялось аж до 2021-го — значит, до Вас на него нарывались максимум единицы из десятков-сотен тысяч. Это к вопросу о хотелках и степени их важности.

BFE>Может не хранить в том смысле, что они могут различаться от закуска к запуску, например.


Это уже какая-то очень динамическая реализация, на основе тех же списков/кортежей. Делать такое для десятков-сотен одних и тех же элементов — чрезмерно. enum как раз и хорош тем, что позволяет создавать разнообразные и одновременно эффективные наборы.

ЕМ>>Это уже не простое перечисление, а некий агрегат.

BFE>Не обязательно делать его одним объектом.

Я ж говорю — надо БД. И непременно в облаке.

BFE>Почему вы пишите значения, если речь о именах?


Потому, что к подобным простым задачам вроде обсуждаемой идеально подходят именно числовые именованные значения, а не какие-то абстрактные.

BFE>Перечисления к аппаратуре имеют весьма опосредствованное отношение.


Отлично подходит для кодирования состояний устройства, внутренних функций, режимов работы, состояний драйвера/обработчика и подобного.

BFE>Зачем упорядоченность?


Тогда это не перечисление, а просто множество. Более общая сущность, не обладающая свойствами перечисления, которые часто бывают полезны.

Больше всего мне нравится перечислимый тип (cardinal) в Pascal/Modula — сами по себе это просто идентификаторы, которые можно использовать только в рамках своего типа, но для них определены порядковый номер (ord) и операции succ/pred, но нет возможности узнать количество элементов.

BFE>Меняется не просто значение, меняется тип, в который может перечисление конвертироваться.


Такие вещи должен отлавливать компилятор. Я всегда выступал за то, чтобы -W4 -Wall подразумевалось по умолчанию.

BFE>Самодостаточного кода намного больше, чем системно-зависимого.


Если кажется, что код не зависит от системы — значит, он зависит от библиотек, которые сопрягают его с системой. Стерильного кода, который вводит с абстрактной клавиатуры и выводит на абстрактный экран или ПУ, в общей массе немного.

ЕМ>>На мой взгляд, для красивого метапрограммирования необходимы средства как функциональной записи, давно продвигаемые в стандарт языка, так и процедурной, которой почему-то избегают любой ценой, будто она заразна.


BFE>Не избегают, а реализовать не могут из-за сложности.


Разумеется — на фоне того хаоса, который они устроили в языке, увязать все это будет непросто.
Re[27]: Как записать такое в современном C++?
От: kov_serg Россия  
Дата: 01.08.24 18:11
Оценка:
Здравствуйте, so5team, Вы писали:

S>Я не понял, вам что, нравится испражняться на публике или вы хвастаетесь собственным скудоумием?


Задел чувства верующего? Ну извини я же не виноват что современный C++ убог чуть более чем полностью.
Re[26]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 01.08.24 18:20
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>Не обязательно писать так примитивно, можно и разного универсального накрутить.

BFE>>Вот чуть более продвинутый пример: здесь
Автор: B0FEE664
Дата: 12.04.24
. На его основе можно написать сколь угодно сложную инициализацию констант.

ЕМ>А можно и базу данных прикрутить...
Я так и сделал
И каждый день — без права на ошибку...
Re[27]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 01.08.24 20:08
Оценка:
Здравствуйте, so5team, Вы писали:

S>А вы много лет продвигали правильный вариант, но ваше мнение игнорировали? Или много лет спонсировали комитет по стандартизации, а он упорно не принимал то, что вы хотели?


Я, по наивности своей, долгое время думал, что там сплошь чертовски умные дядьки, не мне чета, и лучше знают, как развивать язык, а я просто хочу странного. Но постепенно, глядя на его развитие, сильно в этом усомнился. Как, собственно, и в политиках, экономистах, медиках и прочих.

Да что там язык — идея софта для заметок-напоминалок у меня тоже лет двадцать назад сложилась, но все было недосуг, плюс уверенность, что примерно то же самое вот-вот кто-то реализует, ибо все на поверхности. А нет, никто не сподобился. Может, сам как-нибудь возьмусь.

ЕМ>>все это в разных формах было придумано и использовалось давным-давно


S>Примеры можно? Может вы про PL/1?


Лень мне искать конкретные примеры — за много лет попадалось с десяток реализаций минимум. И в PL/1, и в ассемблере System/360, и в содранном с него макроассемблере БЭМШ для БЭСМ-6, и даже (в зачаточном виде) в ассемблерах для PC.

S>сливаться как только речь заходит о конкретике.


Так Вы ж все время требуете примеров кода, а мне лень их искать или специально сочинять, предпочитаю описывать словами. Я не отношусь к числу людей, мозги которых свободно оперируют конструкциями "возьмем S такое, что <трехэтажная формула>".

S>Указатель это такая абстракция в C++, которая скрывает от программиста что из себя представляет адрес на конкретной платформе.


Она это скрывает от программиста, который не хочет этого знать, и может себе позволить такое незнание. Для тех, кто хочет и может, в реализациях обычно достаточно способов привязки. Их все нет смысла тащить в стандарт, чтоб не загромождать его еще больше — разве что некоторые.

ЕМ>>Соответственно, реализации launder для этой платформы должны проверять, где находятся адреса.


S>Может стоило бы подучить матчасть, тем более, что она появилась уже после вашего вхождения в профессию. Просто чтобы глупости не говорить. Хотя бы про launder.


Он мне никогда не был нужен, поэтому я никогда о нем и не читал. Глянул мельком — вот и показалось на фоне Вашего контекста, что он действительно приводит указатель к "нейтральному" виду, допускающему сравнение с чем угодно.

ЕМ>>По-моему, пустой бесконечный цикл нигде не должен считаться нормой. Есть исключения?


S>https://rsdn.org/forum/cpp/8600448.1
Автор: vsb
Дата: 16.09.23


Негодный пример — такой цикл будет жрать энергию и греть МК, поэтому в качестве сколько-нибудь рабочего категорически не годится — нужно вставлять хотя бы вызов типа sleep/yield для конкретной платформы. А если уж кому-то приспичило крутить именно непрерывный, который греет — может вставить туда какие-нибудь "тяжелые" операции, чтоб греть сильнее.

Такой вот непроизвольный защитный барьер получился.
Re[28]: Как записать такое в современном C++?
От: so5team https://stiffstream.com
Дата: 02.08.24 04:41
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

S>>А вы много лет продвигали правильный вариант, но ваше мнение игнорировали? Или много лет спонсировали комитет по стандартизации, а он упорно не принимал то, что вы хотели?


ЕМ>Я, по наивности своей, долгое время думал, что там сплошь чертовски умные дядьки, не мне чета, и лучше знают, как развивать язык, а я просто хочу странного. Но постепенно, глядя на его развитие, сильно в этом усомнился. Как, собственно, и в политиках, экономистах, медиках и прочих.


А вы попробуйте принять участие в развитии языка. Ну или пообщайтесь с теми, кто этим занимается. Пока же вы лишь подтверждаете житейскую мудрость "каждый мнит себя стратегом видя бой со стороны".

ЕМ>Да что там язык — идея софта для заметок-напоминалок у меня тоже лет двадцать назад сложилась, но все было недосуг, плюс уверенность, что примерно то же самое вот-вот кто-то реализует, ибо все на поверхности. А нет, никто не сподобился. Может, сам как-нибудь возьмусь.


Возьмитесь, сделайте, явите миру. Уверяю вас, найдется множество таких же Музыченко, как вы, которые найдут в сделанном вами кучу фатальных недостатков.

ЕМ>>>все это в разных формах было придумано и использовалось давным-давно


S>>Примеры можно? Может вы про PL/1?


ЕМ>Лень мне искать конкретные примеры — за много лет попадалось с десяток реализаций минимум.


Музыченко as is.

ЕМ>И в PL/1


Это в том, который еще во времена своей востребованности стал эталоном того, как делать не нужно? Так себе ориентир.

ЕМ>Так Вы ж все время требуете примеров кода, а мне лень их искать или специально сочинять, предпочитаю описывать словами.


Во-первых, это говорит о том, что вы не разбираетесь в предмете разговора.

Во-вторых, это не оставляет места для предметного общения и шансов на хоть какой-то конструктивный выхлоп.

ЕМ>Я не отношусь к числу людей, мозги которых свободно оперируют конструкциями "возьмем S такое, что <трехэтажная формула>".


Здесь не нужно трехэтажных формул. А вот фрагменты кода, раз уж речь про удобство языка программирования, должны быть. Иначе нет предмета самого разговора.

S>>Указатель это такая абстракция в C++, которая скрывает от программиста что из себя представляет адрес на конкретной платформе.


ЕМ>Она это скрывает от программиста, который не хочет этого знать, и может себе позволить такое незнание. Для тех, кто хочет и может, в реализациях обычно достаточно способов привязки.


И тут мы опять без примеров?

Это во-первых.

А во-вторых, я вам не зря про std::launder и std::start_lifetime_as. Это все о том, что в современном C++ мало присвоить указателю какое-то собственное значение. Еще и нужно компилятору объяснить, что это не абы что. Иначе продвинутый компилятор пошлет все ваши потуги вдоль. Что очень странно для языка, в котором вы видите близость к железу.

ЕМ>>>Соответственно, реализации launder для этой платформы должны проверять, где находятся адреса.


S>>Может стоило бы подучить матчасть, тем более, что она появилась уже после вашего вхождения в профессию. Просто чтобы глупости не говорить. Хотя бы про launder.


ЕМ>Он мне никогда не был нужен, поэтому я никогда о нем и не читал. Глянул мельком — вот и показалось на фоне Вашего контекста, что он действительно приводит указатель к "нейтральному" виду, допускающему сравнение с чем угодно.


Еще одно подтверждение тому, что вы звиздите о том, о чем не имеете понятия.

ЕМ>>>По-моему, пустой бесконечный цикл нигде не должен считаться нормой. Есть исключения?


S>>https://rsdn.org/forum/cpp/8600448.1
Автор: vsb
Дата: 16.09.23


ЕМ>Негодный пример


Он настолько негодный и настолько редкий, что даже пропозал сделали, чтобы это поведение поменять: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2809r1.html
Причем нужно это как раз тем, кто bare metal занимается. Куда уж ближе к железу.

lavrov.jpg
Re[26]: Как записать такое в современном C++?
От: B0FEE664  
Дата: 02.08.24 16:45
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

p2996r4
ЕМ>Как всегда, корявенько. Все пытаются сделать покороче, сэкономить несколько символов, хотя полные конструкции уже давно стали весьма развесистыми. Лучше б обозначать набор свойств типа по аналогии со структурой: T.type_id, T.type_name, T.is_arithmetic, T.num_of_members, T.members [2].is_function, T.members [2].args [3].type_id, и т.п.

Такой синтаксис не подойдёт для случая, когда type_id надо узнать у переменной. Везде придётся писать decltype:
struct A { int i; } a;
decltype(a.i).type_id

Ну, попустим. А дальше что? Какая конструкция переведёт T.type_name обратно в тип?
И каждый день — без права на ошибку...
Re[27]: Как записать такое в современном C++?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 02.08.24 18:37
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Везде придётся писать decltype


Сколько примерно будет этого "везде"? Если много, то имеет смысл сделать сокращение. Но мне почему-то кажется, что decltype, вместе с адекватными операциями над типами, будет компактнее, чем многие нынешние "решения".

BFE>Какая конструкция переведёт T.type_name обратно в тип?


Понятное дело, нужна полная система операций над всеми сущностями, известными во время компиляции (типами, классами, переменными, функциями и прочим).
Re: Как записать такое в современном C++?
От: Умака Кумакаки Ниоткуда  
Дата: 12.08.24 00:35
Оценка: 15 (1)
Здравствуйте, Alekzander, Вы писали:

A>Как это записать в современном C++, чтобы не было performance penalty? Без конструирования контейнера и т.п. Нормальных макросов же (как в Немерле), насколько я понимаю, не завезли?


в с++23 можно так:

if (std::ranges::contains(std::array{
  EAGAIN,
  EWOULDBLOCK,
  EINTR,
  ENOSPC,
  ENOBUFS,
  ENOMEM
}, errno)) {
  // 
};
нормально делай — нормально будет
Отредактировано 12.08.2024 0:38 Умака Кумакаки . Предыдущая версия .
Re[4]: Как записать такое в современном C++?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.09.24 11:56
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Здравствуйте, netch80, Вы писали:


N>>Тут есть одна проблемка: на большинстве систем коды EAGAIN и EWOULDBLOCK сейчас совпадают (такое вот легаси), но есть специфические, где они различны. При совпадении switch выдаёт ошибку.


Pzz>А на BSD совпадает?


Все BSD не смотрел. На фряхе совпадает.
The God is real, unless declared integer.
Re[3]: Как записать такое в современном C++?
От: vdimas Россия  
Дата: 19.11.24 04:11
Оценка:
Здравствуйте, Alekzander, Вы писали:

Pzz>>
Pzz>>    if (errno == EAGAIN) {
Pzz>>        errno = EWOULDBLOCK;
Pzz>>    }
Pzz>>

A>Это будет трудноватенько обобщить. (Может, и к лучшему).

А для какой платформы?
На линухах, BSD и макоси это один и тот же код.
Для виндов есть только WSAEWOULDBLOCK, без WSAEAGAIN.

Если речь про сокеты, то исторически для записи чаще было EWOULDBLOCK (кроме неблокирующего connect, где ожидается готовность по записи), а для чтения EAGAIN (в т.ч. для неблокирующего accept), потом эти константы в POSIX допустили объединить.

Вдогонку, таблица кодов ошибок для различных систем:
https://www.ioplex.com/~miallen/errcmp.html

Трудновато там будет обобщить другое — код ошибки в случае неудачного connect, бо его надо добывать по-разному в разных системах.
А для семантики работы с сокетом лучше просто сравнить и с EWOULDBLOCK и с EAGAIN, т.к. компилятор лишнее сравнение выкинет, если коды одинаковые.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.